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 /* TODO list : 23 * - handling of palettes 24 * - recording (which input devices ?), a cam recorder ? 25 * - lots of messages still need to be handled (cf FIXME) 26 * - synchronization between audio and video (especially for interleaved 27 * files) 28 * - robustness when reading file can be enhanced 29 * - reimplement the AVI handling part with avifile DLL because 30 * "open @1122334 type avivideo alias a" expects an AVIFile/Stream 31 * and MCI_DGV_SET|STATUS_SPEED maps to Rate/Scale 32 * - some files appear to have more than one audio stream (we only play the 33 * first one) 34 * - some files contain an index of audio/video frame. Better use it, 35 * instead of rebuilding it (AVIFile does that already) 36 * - stopping while playing a file with sound blocks until all buffered 37 * audio is played... still should be stopped ASAP 38 */ 39 40 #include <string.h> 41 #include "private_mciavi.h" 42 #include "wine/debug.h" 43 #include "wine/unicode.h" 44 45 WINE_DEFAULT_DEBUG_CHANNEL(mciavi); 46 47 static DWORD MCIAVI_mciStop(UINT, DWORD, LPMCI_GENERIC_PARMS); 48 49 /*======================================================================* 50 * MCI AVI implementation * 51 *======================================================================*/ 52 53 HINSTANCE MCIAVI_hInstance = 0; 54 55 /*********************************************************************** 56 * DllMain (MCIAVI.0) 57 */ 58 BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD fdwReason, LPVOID fImpLoad) 59 { 60 switch (fdwReason) { 61 case DLL_PROCESS_ATTACH: 62 DisableThreadLibraryCalls(hInstDLL); 63 MCIAVI_hInstance = hInstDLL; 64 break; 65 } 66 return TRUE; 67 } 68 69 /************************************************************************** 70 * MCIAVI_drvOpen [internal] 71 */ 72 static DWORD MCIAVI_drvOpen(LPCWSTR str, LPMCI_OPEN_DRIVER_PARMSW modp) 73 { 74 WINE_MCIAVI* wma; 75 static const WCHAR mciAviWStr[] = {'M','C','I','A','V','I',0}; 76 77 TRACE("%s, %p\n", debugstr_w(str), modp); 78 79 /* session instance */ 80 if (!modp) return 0xFFFFFFFF; 81 82 if (!MCIAVI_RegisterClass()) return 0; 83 84 wma = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINE_MCIAVI)); 85 if (!wma) 86 return 0; 87 88 InitializeCriticalSection(&wma->cs); 89 wma->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": WINE_MCIAVI.cs"); 90 wma->hStopEvent = CreateEventW(NULL, FALSE, FALSE, NULL); 91 wma->wDevID = modp->wDeviceID; 92 wma->wCommandTable = mciLoadCommandResource(MCIAVI_hInstance, mciAviWStr, 0); 93 wma->dwStatus = MCI_MODE_NOT_READY; 94 modp->wCustomCommandTable = wma->wCommandTable; 95 modp->wType = MCI_DEVTYPE_DIGITAL_VIDEO; 96 mciSetDriverData(wma->wDevID, (DWORD_PTR)wma); 97 98 return modp->wDeviceID; 99 } 100 101 /************************************************************************** 102 * MCIAVI_drvClose [internal] 103 */ 104 static DWORD MCIAVI_drvClose(DWORD dwDevID) 105 { 106 WINE_MCIAVI *wma; 107 108 TRACE("%04x\n", dwDevID); 109 110 /* finish all outstanding things */ 111 MCIAVI_mciClose(dwDevID, MCI_WAIT, NULL); 112 113 wma = (WINE_MCIAVI*)mciGetDriverData(dwDevID); 114 115 if (wma) { 116 MCIAVI_UnregisterClass(); 117 118 EnterCriticalSection(&wma->cs); 119 120 mciSetDriverData(dwDevID, 0); 121 mciFreeCommandResource(wma->wCommandTable); 122 123 CloseHandle(wma->hStopEvent); 124 125 LeaveCriticalSection(&wma->cs); 126 wma->cs.DebugInfo->Spare[0] = 0; 127 DeleteCriticalSection(&wma->cs); 128 129 HeapFree(GetProcessHeap(), 0, wma); 130 return 1; 131 } 132 return (dwDevID == 0xFFFFFFFF) ? 1 : 0; 133 } 134 135 /************************************************************************** 136 * MCIAVI_drvConfigure [internal] 137 */ 138 static DWORD MCIAVI_drvConfigure(DWORD dwDevID) 139 { 140 WINE_MCIAVI *wma; 141 142 TRACE("%04x\n", dwDevID); 143 144 MCIAVI_mciStop(dwDevID, MCI_WAIT, NULL); 145 146 wma = (WINE_MCIAVI*)mciGetDriverData(dwDevID); 147 148 if (wma) { 149 MessageBoxA(0, "Sample AVI Wine Driver !", "MM-Wine Driver", MB_OK); 150 return 1; 151 } 152 return 0; 153 } 154 155 /************************************************************************** 156 * MCIAVI_mciGetOpenDev [internal] 157 */ 158 WINE_MCIAVI* MCIAVI_mciGetOpenDev(UINT wDevID) 159 { 160 WINE_MCIAVI* wma = (WINE_MCIAVI*)mciGetDriverData(wDevID); 161 162 if (wma == NULL || wma->nUseCount == 0) { 163 WARN("Invalid wDevID=%u\n", wDevID); 164 return 0; 165 } 166 return wma; 167 } 168 169 static void MCIAVI_CleanUp(WINE_MCIAVI* wma) 170 { 171 /* to prevent handling in WindowProc */ 172 wma->dwStatus = MCI_MODE_NOT_READY; 173 if (wma->hFile) { 174 mmioClose(wma->hFile, 0); 175 wma->hFile = 0; 176 177 HeapFree(GetProcessHeap(), 0, wma->lpFileName); 178 wma->lpFileName = NULL; 179 180 HeapFree(GetProcessHeap(), 0, wma->lpVideoIndex); 181 wma->lpVideoIndex = NULL; 182 HeapFree(GetProcessHeap(), 0, wma->lpAudioIndex); 183 wma->lpAudioIndex = NULL; 184 if (wma->hic) ICClose(wma->hic); 185 wma->hic = 0; 186 HeapFree(GetProcessHeap(), 0, wma->inbih); 187 wma->inbih = NULL; 188 HeapFree(GetProcessHeap(), 0, wma->outbih); 189 wma->outbih = NULL; 190 HeapFree(GetProcessHeap(), 0, wma->indata); 191 wma->indata = NULL; 192 HeapFree(GetProcessHeap(), 0, wma->outdata); 193 wma->outdata = NULL; 194 if (wma->hbmFrame) DeleteObject(wma->hbmFrame); 195 wma->hbmFrame = 0; 196 if (wma->hWnd) DestroyWindow(wma->hWnd); 197 wma->hWnd = 0; 198 199 HeapFree(GetProcessHeap(), 0, wma->lpWaveFormat); 200 wma->lpWaveFormat = 0; 201 202 memset(&wma->mah, 0, sizeof(wma->mah)); 203 memset(&wma->ash_video, 0, sizeof(wma->ash_video)); 204 memset(&wma->ash_audio, 0, sizeof(wma->ash_audio)); 205 wma->dwCurrVideoFrame = wma->dwCurrAudioBlock = 0; 206 wma->dwCachedFrame = -1; 207 } 208 } 209 210 /*************************************************************************** 211 * MCIAVI_mciOpen [internal] 212 */ 213 static DWORD MCIAVI_mciOpen(UINT wDevID, DWORD dwFlags, 214 LPMCI_DGV_OPEN_PARMSW lpOpenParms) 215 { 216 WINE_MCIAVI *wma; 217 LRESULT dwRet = 0; 218 219 TRACE("(%04x, %08X, %p)\n", wDevID, dwFlags, lpOpenParms); 220 221 if (lpOpenParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK; 222 223 wma = (WINE_MCIAVI *)mciGetDriverData(wDevID); 224 if (wma == NULL) return MCIERR_INVALID_DEVICE_ID; 225 226 EnterCriticalSection(&wma->cs); 227 228 if (wma->nUseCount > 0) { 229 /* The driver is already open on this channel */ 230 /* If the driver was opened shareable before and this open specifies */ 231 /* shareable then increment the use count */ 232 if (wma->fShareable && (dwFlags & MCI_OPEN_SHAREABLE)) 233 ++wma->nUseCount; 234 else 235 { 236 LeaveCriticalSection(&wma->cs); 237 return MCIERR_MUST_USE_SHAREABLE; 238 } 239 } else { 240 wma->nUseCount = 1; 241 wma->fShareable = dwFlags & MCI_OPEN_SHAREABLE; 242 } 243 244 wma->dwStatus = MCI_MODE_NOT_READY; 245 246 if (dwFlags & MCI_OPEN_ELEMENT) { 247 if (dwFlags & MCI_OPEN_ELEMENT_ID) { 248 /* could it be that (DWORD)lpOpenParms->lpstrElementName 249 * contains the hFile value ? 250 */ 251 dwRet = MCIERR_UNRECOGNIZED_COMMAND; 252 } else if (lpOpenParms->lpstrElementName && lpOpenParms->lpstrElementName[0]) { 253 /* FIXME : what should be done id wma->hFile is already != 0, or the driver is playin' */ 254 TRACE("MCI_OPEN_ELEMENT %s!\n", debugstr_w(lpOpenParms->lpstrElementName)); 255 256 wma->lpFileName = HeapAlloc(GetProcessHeap(), 0, (strlenW(lpOpenParms->lpstrElementName) + 1) * sizeof(WCHAR)); 257 strcpyW(wma->lpFileName, lpOpenParms->lpstrElementName); 258 259 if (lpOpenParms->lpstrElementName[0] == '@') { 260 /* The file name @11223344 encodes an AVIFile handle in decimal notation 261 * in Win3.1 and w2k/NT, but this feature is absent in win95 (KB140750). 262 * wma->hFile = LongToHandle(strtolW(lpOpenParms->lpstrElementName+1, NULL, 10)); */ 263 FIXME("Using AVIFile/Stream %s NIY\n", debugstr_w(lpOpenParms->lpstrElementName)); 264 } 265 wma->hFile = mmioOpenW(lpOpenParms->lpstrElementName, NULL, 266 MMIO_ALLOCBUF | MMIO_DENYWRITE | MMIO_READ); 267 268 if (wma->hFile == 0) { 269 WARN("can't find file=%s!\n", debugstr_w(lpOpenParms->lpstrElementName)); 270 dwRet = MCIERR_FILE_NOT_FOUND; 271 } else { 272 if (!MCIAVI_GetInfo(wma)) 273 dwRet = MCIERR_INVALID_FILE; 274 else if (!MCIAVI_OpenVideo(wma)) 275 dwRet = MCIERR_CANNOT_LOAD_DRIVER; 276 else if (!MCIAVI_CreateWindow(wma, dwFlags, lpOpenParms)) 277 dwRet = MCIERR_CREATEWINDOW; 278 } 279 } else { 280 FIXME("Don't record yet\n"); 281 dwRet = MCIERR_UNSUPPORTED_FUNCTION; 282 } 283 } 284 285 if (dwRet == 0) { 286 TRACE("lpOpenParms->wDeviceID = %04x\n", lpOpenParms->wDeviceID); 287 288 wma->dwStatus = MCI_MODE_STOP; 289 wma->dwMciTimeFormat = MCI_FORMAT_FRAMES; 290 } else { 291 MCIAVI_CleanUp(wma); 292 } 293 294 LeaveCriticalSection(&wma->cs); 295 296 if (!dwRet && (dwFlags & MCI_NOTIFY)) { 297 mciDriverNotify(HWND_32(LOWORD(lpOpenParms->dwCallback)), 298 wDevID, MCI_NOTIFY_SUCCESSFUL); 299 } 300 return dwRet; 301 } 302 303 /*************************************************************************** 304 * MCIAVI_mciClose [internal] 305 */ 306 DWORD MCIAVI_mciClose(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms) 307 { 308 WINE_MCIAVI *wma; 309 DWORD dwRet = 0; 310 311 TRACE("(%04x, %08X, %p)\n", wDevID, dwFlags, lpParms); 312 313 wma = MCIAVI_mciGetOpenDev(wDevID); 314 if (wma == NULL) return MCIERR_INVALID_DEVICE_ID; 315 316 MCIAVI_mciStop(wDevID, MCI_WAIT, NULL); 317 318 EnterCriticalSection(&wma->cs); 319 320 if (wma->nUseCount == 1) { 321 MCIAVI_CleanUp(wma); 322 323 if ((dwFlags & MCI_NOTIFY) && lpParms) { 324 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)), 325 wDevID, 326 MCI_NOTIFY_SUCCESSFUL); 327 } 328 LeaveCriticalSection(&wma->cs); 329 return dwRet; 330 } 331 wma->nUseCount--; 332 333 LeaveCriticalSection(&wma->cs); 334 return dwRet; 335 } 336 337 static double currenttime_us(void) 338 { 339 LARGE_INTEGER lc, lf; 340 QueryPerformanceCounter(&lc); 341 QueryPerformanceFrequency(&lf); 342 return (lc.QuadPart * 1000000) / lf.QuadPart; 343 } 344 345 /*************************************************************************** 346 * MCIAVI_player [internal] 347 */ 348 static DWORD MCIAVI_player(WINE_MCIAVI *wma, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms) 349 { 350 DWORD dwRet; 351 LPWAVEHDR waveHdr = NULL; 352 unsigned i, nHdr = 0; 353 DWORD numEvents = 1; 354 HANDLE events[2]; 355 double next_frame_us; 356 BOOL wait_audio = TRUE; 357 358 EnterCriticalSection(&wma->cs); 359 360 if (wma->dwToVideoFrame <= wma->dwCurrVideoFrame) 361 { 362 dwRet = 0; 363 goto mci_play_done; 364 } 365 366 events[0] = wma->hStopEvent; 367 if (wma->lpWaveFormat) { 368 if (MCIAVI_OpenAudio(wma, &nHdr, &waveHdr) != 0) 369 { 370 /* can't play audio */ 371 HeapFree(GetProcessHeap(), 0, wma->lpWaveFormat); 372 wma->lpWaveFormat = NULL; 373 } 374 else 375 { 376 /* fill the queue with as many wave headers as possible */ 377 MCIAVI_PlayAudioBlocks(wma, nHdr, waveHdr); 378 events[1] = wma->hEvent; 379 numEvents = 2; 380 } 381 } 382 383 next_frame_us = currenttime_us(); 384 while (wma->dwStatus == MCI_MODE_PLAY) 385 { 386 HDC hDC; 387 double tc, delta; 388 DWORD ret; 389 390 tc = currenttime_us(); 391 392 hDC = wma->hWndPaint ? GetDC(wma->hWndPaint) : 0; 393 if (hDC) 394 { 395 while(next_frame_us <= tc && wma->dwCurrVideoFrame < wma->dwToVideoFrame){ 396 double dur; 397 dur = MCIAVI_PaintFrame(wma, hDC); 398 ++wma->dwCurrVideoFrame; 399 if(!dur) 400 break; 401 next_frame_us += dur; 402 TRACE("next_frame: %f\n", next_frame_us); 403 } 404 ReleaseDC(wma->hWndPaint, hDC); 405 } 406 if (wma->dwCurrVideoFrame >= wma->dwToVideoFrame) 407 { 408 if (!(dwFlags & MCI_DGV_PLAY_REPEAT)) 409 break; 410 TRACE("repeat media as requested\n"); 411 wma->dwCurrVideoFrame = wma->dwCurrAudioBlock = 0; 412 } 413 414 if (wma->lpWaveFormat) 415 MCIAVI_PlayAudioBlocks(wma, nHdr, waveHdr); 416 417 tc = currenttime_us(); 418 if (tc < next_frame_us) 419 delta = next_frame_us - tc; 420 else 421 delta = 0; 422 423 /* check if the playback was cancelled */ 424 if ((wma->mci_break.flags & MCI_BREAK_KEY) && 425 (GetAsyncKeyState(wma->mci_break.parms.nVirtKey) & 0x8000)) 426 { 427 if (!(wma->mci_break.flags & MCI_BREAK_HWND) || 428 GetForegroundWindow() == wma->mci_break.parms.hwndBreak) 429 { 430 /* we queue audio blocks ahead so ignore them otherwise the audio 431 * will keep playing until the buffer is empty */ 432 wait_audio = FALSE; 433 434 TRACE("playback cancelled using break key\n"); 435 break; 436 } 437 } 438 439 LeaveCriticalSection(&wma->cs); 440 ret = WaitForMultipleObjects(numEvents, events, FALSE, delta / 1000); 441 EnterCriticalSection(&wma->cs); 442 if (ret == WAIT_OBJECT_0 || wma->dwStatus != MCI_MODE_PLAY) break; 443 } 444 445 if (wma->lpWaveFormat) 446 { 447 if (wait_audio) 448 while (wma->dwEventCount != nHdr - 1) 449 { 450 LeaveCriticalSection(&wma->cs); 451 Sleep(100); 452 EnterCriticalSection(&wma->cs); 453 } 454 455 /* just to get rid of some race conditions between play, stop and pause */ 456 LeaveCriticalSection(&wma->cs); 457 waveOutReset(wma->hWave); 458 EnterCriticalSection(&wma->cs); 459 460 for (i = 0; i < nHdr; i++) 461 waveOutUnprepareHeader(wma->hWave, &waveHdr[i], sizeof(WAVEHDR)); 462 } 463 464 dwRet = 0; 465 466 if (wma->lpWaveFormat) { 467 HeapFree(GetProcessHeap(), 0, waveHdr); 468 469 if (wma->hWave) { 470 LeaveCriticalSection(&wma->cs); 471 waveOutClose(wma->hWave); 472 EnterCriticalSection(&wma->cs); 473 wma->hWave = 0; 474 } 475 CloseHandle(wma->hEvent); 476 } 477 478 mci_play_done: 479 wma->dwStatus = MCI_MODE_STOP; 480 481 if (dwFlags & MCI_NOTIFY) { 482 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback); 483 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)), 484 wma->wDevID, MCI_NOTIFY_SUCCESSFUL); 485 } 486 LeaveCriticalSection(&wma->cs); 487 return dwRet; 488 } 489 490 struct MCIAVI_play_data 491 { 492 WINE_MCIAVI *wma; 493 DWORD flags; 494 MCI_PLAY_PARMS params; /* FIXME: notify via wma->hCallback like the other MCI drivers */ 495 }; 496 497 /* 498 * MCIAVI_mciPlay_thread 499 * 500 * FIXME: probably should use a common worker thread created at the driver 501 * load time and queue all async commands to it. 502 */ 503 static DWORD WINAPI MCIAVI_mciPlay_thread(LPVOID arg) 504 { 505 struct MCIAVI_play_data *data = (struct MCIAVI_play_data *)arg; 506 DWORD ret; 507 508 TRACE("In thread before async play command (id %u, flags %08x)\n", data->wma->wDevID, data->flags); 509 ret = MCIAVI_player(data->wma, data->flags, &data->params); 510 TRACE("In thread after async play command (id %u, flags %08x)\n", data->wma->wDevID, data->flags); 511 512 HeapFree(GetProcessHeap(), 0, data); 513 return ret; 514 } 515 516 /* 517 * MCIAVI_mciPlay_async 518 */ 519 static DWORD MCIAVI_mciPlay_async(WINE_MCIAVI *wma, DWORD dwFlags, LPMCI_PLAY_PARMS lpParams) 520 { 521 HANDLE handle; 522 struct MCIAVI_play_data *data = HeapAlloc(GetProcessHeap(), 0, sizeof(struct MCIAVI_play_data)); 523 524 if (!data) return MCIERR_OUT_OF_MEMORY; 525 526 data->wma = wma; 527 data->flags = dwFlags; 528 if (dwFlags & MCI_NOTIFY) 529 data->params.dwCallback = lpParams->dwCallback; 530 531 if (!(handle = CreateThread(NULL, 0, MCIAVI_mciPlay_thread, data, 0, NULL))) 532 { 533 WARN("Couldn't create thread for async play, playing synchronously\n"); 534 return MCIAVI_mciPlay_thread(data); 535 } 536 SetThreadPriority(handle, THREAD_PRIORITY_TIME_CRITICAL); 537 CloseHandle(handle); 538 return 0; 539 } 540 541 /*************************************************************************** 542 * MCIAVI_mciPlay [internal] 543 */ 544 static DWORD MCIAVI_mciPlay(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms) 545 { 546 WINE_MCIAVI *wma; 547 DWORD dwRet; 548 DWORD dwFromFrame, dwToFrame; 549 550 TRACE("(%04x, %08X, %p)\n", wDevID, dwFlags, lpParms); 551 552 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK; 553 554 wma = MCIAVI_mciGetOpenDev(wDevID); 555 if (wma == NULL) return MCIERR_INVALID_DEVICE_ID; 556 if (dwFlags & MCI_DGV_PLAY_REVERSE) return MCIERR_UNSUPPORTED_FUNCTION; 557 if (dwFlags & MCI_TEST) return 0; 558 559 if (dwFlags & (MCI_MCIAVI_PLAY_WINDOW|MCI_MCIAVI_PLAY_FULLSCREEN|MCI_MCIAVI_PLAY_FULLBY2)) 560 FIXME("Unsupported flag %08x\n", dwFlags); 561 562 EnterCriticalSection(&wma->cs); 563 564 if (!wma->hFile) 565 { 566 LeaveCriticalSection(&wma->cs); 567 return MCIERR_FILE_NOT_FOUND; 568 } 569 if (!wma->hWndPaint) 570 { 571 LeaveCriticalSection(&wma->cs); 572 return MCIERR_NO_WINDOW; 573 } 574 575 dwFromFrame = wma->dwCurrVideoFrame; 576 dwToFrame = wma->dwPlayableVideoFrames - 1; 577 578 if (dwFlags & MCI_FROM) { 579 dwFromFrame = MCIAVI_ConvertTimeFormatToFrame(wma, lpParms->dwFrom); 580 } 581 if (dwFlags & MCI_TO) { 582 dwToFrame = MCIAVI_ConvertTimeFormatToFrame(wma, lpParms->dwTo); 583 } 584 if (dwToFrame >= wma->dwPlayableVideoFrames) 585 dwToFrame = wma->dwPlayableVideoFrames - 1; 586 587 TRACE("Playing from frame=%u to frame=%u\n", dwFromFrame, dwToFrame); 588 589 wma->dwCurrVideoFrame = dwFromFrame; 590 wma->dwToVideoFrame = dwToFrame; 591 592 LeaveCriticalSection(&wma->cs); 593 594 if (!(GetWindowLongW(wma->hWndPaint, GWL_STYLE) & WS_VISIBLE)) 595 ShowWindow(wma->hWndPaint, SW_SHOWNA); 596 597 EnterCriticalSection(&wma->cs); 598 599 /* if already playing exit */ 600 if (wma->dwStatus == MCI_MODE_PLAY) 601 { 602 LeaveCriticalSection(&wma->cs); 603 return 0; 604 } 605 606 wma->dwStatus = MCI_MODE_PLAY; 607 608 LeaveCriticalSection(&wma->cs); 609 610 if (dwFlags & MCI_WAIT) 611 return MCIAVI_player(wma, dwFlags, lpParms); 612 613 dwRet = MCIAVI_mciPlay_async(wma, dwFlags, lpParms); 614 615 if (dwRet) { 616 EnterCriticalSection(&wma->cs); 617 wma->dwStatus = MCI_MODE_STOP; 618 LeaveCriticalSection(&wma->cs); 619 } 620 return dwRet; 621 } 622 623 /*************************************************************************** 624 * MCIAVI_mciStop [internal] 625 */ 626 static DWORD MCIAVI_mciStop(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms) 627 { 628 WINE_MCIAVI *wma; 629 DWORD dwRet = 0; 630 631 TRACE("(%04x, %08X, %p)\n", wDevID, dwFlags, lpParms); 632 633 wma = MCIAVI_mciGetOpenDev(wDevID); 634 if (wma == NULL) return MCIERR_INVALID_DEVICE_ID; 635 if (dwFlags & MCI_TEST) return 0; 636 637 EnterCriticalSection(&wma->cs); 638 639 TRACE("current status %04x\n", wma->dwStatus); 640 641 switch (wma->dwStatus) { 642 case MCI_MODE_PLAY: 643 case MCI_MODE_RECORD: 644 LeaveCriticalSection(&wma->cs); 645 SetEvent(wma->hStopEvent); 646 EnterCriticalSection(&wma->cs); 647 /* fall through */ 648 case MCI_MODE_PAUSE: 649 /* Since our wave notification callback takes the lock, 650 * we must release it before resetting the device */ 651 LeaveCriticalSection(&wma->cs); 652 dwRet = waveOutReset(wma->hWave); 653 EnterCriticalSection(&wma->cs); 654 /* fall through */ 655 default: 656 do /* one more chance for an async thread to finish */ 657 { 658 LeaveCriticalSection(&wma->cs); 659 Sleep(10); 660 EnterCriticalSection(&wma->cs); 661 } while (wma->dwStatus != MCI_MODE_STOP); 662 663 break; 664 665 case MCI_MODE_NOT_READY: 666 break; 667 } 668 669 if ((dwFlags & MCI_NOTIFY) && lpParms) { 670 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)), 671 wDevID, MCI_NOTIFY_SUCCESSFUL); 672 } 673 LeaveCriticalSection(&wma->cs); 674 return dwRet; 675 } 676 677 /*************************************************************************** 678 * MCIAVI_mciPause [internal] 679 */ 680 static DWORD MCIAVI_mciPause(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms) 681 { 682 WINE_MCIAVI *wma; 683 684 TRACE("(%04x, %08X, %p)\n", wDevID, dwFlags, lpParms); 685 686 wma = MCIAVI_mciGetOpenDev(wDevID); 687 if (wma == NULL) return MCIERR_INVALID_DEVICE_ID; 688 if (dwFlags & MCI_TEST) return 0; 689 690 EnterCriticalSection(&wma->cs); 691 692 if (wma->dwStatus == MCI_MODE_PLAY) 693 wma->dwStatus = MCI_MODE_PAUSE; 694 695 if (wma->lpWaveFormat) { 696 LeaveCriticalSection(&wma->cs); 697 return waveOutPause(wma->hWave); 698 } 699 700 LeaveCriticalSection(&wma->cs); 701 return 0; 702 } 703 704 /*************************************************************************** 705 * MCIAVI_mciResume [internal] 706 */ 707 static DWORD MCIAVI_mciResume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms) 708 { 709 WINE_MCIAVI *wma; 710 711 TRACE("(%04x, %08X, %p)\n", wDevID, dwFlags, lpParms); 712 713 wma = MCIAVI_mciGetOpenDev(wDevID); 714 if (wma == NULL) return MCIERR_INVALID_DEVICE_ID; 715 if (dwFlags & MCI_TEST) return 0; 716 717 EnterCriticalSection(&wma->cs); 718 719 if (wma->dwStatus == MCI_MODE_PAUSE) 720 wma->dwStatus = MCI_MODE_PLAY; 721 722 if (wma->lpWaveFormat) { 723 LeaveCriticalSection(&wma->cs); 724 return waveOutRestart(wma->hWave); 725 } 726 727 LeaveCriticalSection(&wma->cs); 728 return 0; 729 } 730 731 /*************************************************************************** 732 * MCIAVI_mciSeek [internal] 733 */ 734 static DWORD MCIAVI_mciSeek(UINT wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms) 735 { 736 WINE_MCIAVI *wma; 737 DWORD position; 738 739 TRACE("(%04x, %08X, %p)\n", wDevID, dwFlags, lpParms); 740 741 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK; 742 743 wma = MCIAVI_mciGetOpenDev(wDevID); 744 if (wma == NULL) return MCIERR_INVALID_DEVICE_ID; 745 746 position = dwFlags & (MCI_SEEK_TO_START|MCI_SEEK_TO_END|MCI_TO); 747 if (!position) return MCIERR_MISSING_PARAMETER; 748 if (position&(position-1)) return MCIERR_FLAGS_NOT_COMPATIBLE; 749 750 if (dwFlags & MCI_TO) { 751 position = MCIAVI_ConvertTimeFormatToFrame(wma, lpParms->dwTo); 752 if (position >= wma->dwPlayableVideoFrames) 753 return MCIERR_OUTOFRANGE; 754 } else if (dwFlags & MCI_SEEK_TO_START) { 755 position = 0; 756 } else { 757 position = wma->dwPlayableVideoFrames - 1; 758 } 759 if (dwFlags & MCI_TEST) return 0; 760 761 MCIAVI_mciStop(wDevID, MCI_WAIT, NULL); 762 763 EnterCriticalSection(&wma->cs); 764 765 wma->dwCurrVideoFrame = position; 766 TRACE("Seeking to frame=%u\n", wma->dwCurrVideoFrame); 767 768 if (dwFlags & MCI_NOTIFY) { 769 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)), 770 wDevID, MCI_NOTIFY_SUCCESSFUL); 771 } 772 LeaveCriticalSection(&wma->cs); 773 return 0; 774 } 775 776 /***************************************************************************** 777 * MCIAVI_mciLoad [internal] 778 */ 779 static DWORD MCIAVI_mciLoad(UINT wDevID, DWORD dwFlags, LPMCI_DGV_LOAD_PARMSW lpParms) 780 { 781 WINE_MCIAVI *wma; 782 783 FIXME("(%04x, %08x, %p) : stub\n", wDevID, dwFlags, lpParms); 784 785 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK; 786 787 wma = MCIAVI_mciGetOpenDev(wDevID); 788 if (wma == NULL) return MCIERR_INVALID_DEVICE_ID; 789 790 return MCIERR_UNSUPPORTED_FUNCTION; /* like w2k */ 791 } 792 793 /****************************************************************************** 794 * MCIAVI_mciRealize [internal] 795 */ 796 static DWORD MCIAVI_mciRealize(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms) 797 { 798 WINE_MCIAVI *wma; 799 800 FIXME("(%04x, %08x, %p) : stub\n", wDevID, dwFlags, lpParms); 801 802 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK; 803 804 wma = MCIAVI_mciGetOpenDev(wDevID); 805 if (wma == NULL) return MCIERR_INVALID_DEVICE_ID; 806 if (dwFlags & MCI_TEST) return 0; 807 808 return 0; 809 } 810 811 /****************************************************************************** 812 * MCIAVI_mciUpdate [internal] 813 */ 814 static DWORD MCIAVI_mciUpdate(UINT wDevID, DWORD dwFlags, LPMCI_DGV_UPDATE_PARMS lpParms) 815 { 816 WINE_MCIAVI *wma; 817 818 TRACE("%04x, %08x, %p\n", wDevID, dwFlags, lpParms); 819 820 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK; 821 822 wma = MCIAVI_mciGetOpenDev(wDevID); 823 if (wma == NULL) return MCIERR_INVALID_DEVICE_ID; 824 /* Ignore MCI_TEST flag. */ 825 826 EnterCriticalSection(&wma->cs); 827 828 if (dwFlags & MCI_DGV_UPDATE_HDC) 829 MCIAVI_PaintFrame(wma, lpParms->hDC); 830 831 LeaveCriticalSection(&wma->cs); 832 833 return 0; 834 } 835 836 /****************************************************************************** 837 * MCIAVI_mciStep [internal] 838 */ 839 static DWORD MCIAVI_mciStep(UINT wDevID, DWORD dwFlags, LPMCI_DGV_STEP_PARMS lpParms) 840 { 841 WINE_MCIAVI *wma; 842 DWORD position; 843 int delta = 1; 844 845 TRACE("(%04x, %08x, %p)\n", wDevID, dwFlags, lpParms); 846 847 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK; 848 849 wma = MCIAVI_mciGetOpenDev(wDevID); 850 if (wma == NULL) return MCIERR_INVALID_DEVICE_ID; 851 852 if (dwFlags & MCI_DGV_STEP_FRAMES) delta = lpParms->dwFrames; 853 if (dwFlags & MCI_DGV_STEP_REVERSE) delta = -delta; 854 position = wma->dwCurrVideoFrame + delta; 855 if (position >= wma->dwPlayableVideoFrames) return MCIERR_OUTOFRANGE; 856 if (dwFlags & MCI_TEST) return 0; 857 858 MCIAVI_mciStop(wDevID, MCI_WAIT, NULL); 859 860 EnterCriticalSection(&wma->cs); 861 862 wma->dwCurrVideoFrame = position; 863 TRACE("Stepping to frame=%u\n", wma->dwCurrVideoFrame); 864 865 if (dwFlags & MCI_NOTIFY) { 866 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)), 867 wDevID, MCI_NOTIFY_SUCCESSFUL); 868 } 869 LeaveCriticalSection(&wma->cs); 870 return 0; 871 } 872 873 /****************************************************************************** 874 * MCIAVI_mciCue [internal] 875 */ 876 static DWORD MCIAVI_mciCue(UINT wDevID, DWORD dwFlags, LPMCI_DGV_CUE_PARMS lpParms) 877 { 878 WINE_MCIAVI *wma; 879 880 FIXME("(%04x, %08x, %p) : stub\n", wDevID, dwFlags, lpParms); 881 882 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK; 883 884 wma = MCIAVI_mciGetOpenDev(wDevID); 885 if (wma == NULL) return MCIERR_INVALID_DEVICE_ID; 886 if (dwFlags & MCI_DGV_CUE_INPUT) return MCIERR_UNSUPPORTED_FUNCTION; 887 if (dwFlags & MCI_TEST) return 0; 888 889 return 0; 890 } 891 892 /****************************************************************************** 893 * MCIAVI_mciBreak [internal] 894 */ 895 static DWORD MCIAVI_mciBreak(UINT wDevID, DWORD dwFlags, LPMCI_BREAK_PARMS lpParms) 896 { 897 WINE_MCIAVI *wma; 898 899 TRACE("(%04x, %08x, %p)\n", wDevID, dwFlags, lpParms); 900 901 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK; 902 903 wma = MCIAVI_mciGetOpenDev(wDevID); 904 if (wma == NULL) return MCIERR_INVALID_DEVICE_ID; 905 906 EnterCriticalSection(&wma->cs); 907 908 wma->mci_break.flags = dwFlags; 909 wma->mci_break.parms = *lpParms; 910 911 LeaveCriticalSection(&wma->cs); 912 913 return 0; 914 } 915 916 /****************************************************************************** 917 * MCIAVI_mciSetAudio [internal] 918 */ 919 static DWORD MCIAVI_mciSetAudio(UINT wDevID, DWORD dwFlags, LPMCI_DGV_SETAUDIO_PARMSW lpParms) 920 { 921 WINE_MCIAVI *wma; 922 923 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK; 924 925 FIXME("(%04x, %08x, %p) Item %04x: stub\n", wDevID, dwFlags, lpParms, dwFlags & MCI_DGV_SETAUDIO_ITEM ? lpParms->dwItem : 0); 926 927 wma = MCIAVI_mciGetOpenDev(wDevID); 928 if (wma == NULL) return MCIERR_INVALID_DEVICE_ID; 929 930 return 0; 931 } 932 933 /****************************************************************************** 934 * MCIAVI_mciSignal [internal] 935 */ 936 static DWORD MCIAVI_mciSignal(UINT wDevID, DWORD dwFlags, LPMCI_DGV_SIGNAL_PARMS lpParms) 937 { 938 WINE_MCIAVI *wma; 939 940 FIXME("(%04x, %08x, %p) : stub\n", wDevID, dwFlags, lpParms); 941 942 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK; 943 944 wma = MCIAVI_mciGetOpenDev(wDevID); 945 if (wma == NULL) return MCIERR_INVALID_DEVICE_ID; 946 947 return 0; 948 } 949 950 /****************************************************************************** 951 * MCIAVI_mciSetVideo [internal] 952 */ 953 static DWORD MCIAVI_mciSetVideo(UINT wDevID, DWORD dwFlags, LPMCI_DGV_SETVIDEO_PARMSW lpParms) 954 { 955 WINE_MCIAVI *wma; 956 957 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK; 958 959 FIXME("(%04x, %08x, %p) Item %04x: stub\n", wDevID, dwFlags, lpParms, dwFlags & MCI_DGV_SETVIDEO_ITEM ? lpParms->dwItem : 0); 960 961 wma = MCIAVI_mciGetOpenDev(wDevID); 962 if (wma == NULL) return MCIERR_INVALID_DEVICE_ID; 963 964 return 0; 965 } 966 967 /****************************************************************************** 968 * MCIAVI_mciConfigure [internal] 969 */ 970 static DWORD MCIAVI_mciConfigure(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms) 971 { 972 WINE_MCIAVI *wma; 973 974 FIXME("(%04x, %08x, %p) : stub\n", wDevID, dwFlags, lpParms); 975 976 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK; 977 978 wma = MCIAVI_mciGetOpenDev(wDevID); 979 if (wma == NULL) return MCIERR_INVALID_DEVICE_ID; 980 if (dwFlags & MCI_TEST) return 0; 981 982 return 0; 983 } 984 985 /*======================================================================* 986 * MCI AVI entry points * 987 *======================================================================*/ 988 989 /************************************************************************** 990 * DriverProc (MCIAVI.@) 991 */ 992 LRESULT CALLBACK MCIAVI_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg, 993 LPARAM dwParam1, LPARAM dwParam2) 994 { 995 TRACE("(%08lX, %p, %08X, %08lX, %08lX)\n", 996 dwDevID, hDriv, wMsg, dwParam1, dwParam2); 997 998 switch (wMsg) { 999 case DRV_LOAD: return 1; 1000 case DRV_FREE: return 1; 1001 case DRV_OPEN: return MCIAVI_drvOpen((LPCWSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSW)dwParam2); 1002 case DRV_CLOSE: return MCIAVI_drvClose(dwDevID); 1003 case DRV_ENABLE: return 1; 1004 case DRV_DISABLE: return 1; 1005 case DRV_QUERYCONFIGURE: return 1; 1006 case DRV_CONFIGURE: return MCIAVI_drvConfigure(dwDevID); 1007 case DRV_INSTALL: return DRVCNF_RESTART; 1008 case DRV_REMOVE: return DRVCNF_RESTART; 1009 } 1010 1011 /* session instance */ 1012 if (dwDevID == 0xFFFFFFFF) return 1; 1013 1014 switch (wMsg) { 1015 case MCI_OPEN_DRIVER: return MCIAVI_mciOpen (dwDevID, dwParam1, (LPMCI_DGV_OPEN_PARMSW) dwParam2); 1016 case MCI_CLOSE_DRIVER: return MCIAVI_mciClose (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2); 1017 case MCI_PLAY: return MCIAVI_mciPlay (dwDevID, dwParam1, (LPMCI_PLAY_PARMS) dwParam2); 1018 case MCI_STOP: return MCIAVI_mciStop (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2); 1019 case MCI_SET: return MCIAVI_mciSet (dwDevID, dwParam1, (LPMCI_DGV_SET_PARMS) dwParam2); 1020 case MCI_PAUSE: return MCIAVI_mciPause (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2); 1021 case MCI_RESUME: return MCIAVI_mciResume (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2); 1022 case MCI_STATUS: return MCIAVI_mciStatus (dwDevID, dwParam1, (LPMCI_DGV_STATUS_PARMSW) dwParam2); 1023 case MCI_GETDEVCAPS: return MCIAVI_mciGetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS) dwParam2); 1024 case MCI_INFO: return MCIAVI_mciInfo (dwDevID, dwParam1, (LPMCI_DGV_INFO_PARMSW) dwParam2); 1025 case MCI_SEEK: return MCIAVI_mciSeek (dwDevID, dwParam1, (LPMCI_SEEK_PARMS) dwParam2); 1026 case MCI_PUT: return MCIAVI_mciPut (dwDevID, dwParam1, (LPMCI_DGV_PUT_PARMS) dwParam2); 1027 case MCI_WINDOW: return MCIAVI_mciWindow (dwDevID, dwParam1, (LPMCI_DGV_WINDOW_PARMSW) dwParam2); 1028 case MCI_LOAD: return MCIAVI_mciLoad (dwDevID, dwParam1, (LPMCI_DGV_LOAD_PARMSW) dwParam2); 1029 case MCI_REALIZE: return MCIAVI_mciRealize (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2); 1030 case MCI_UPDATE: return MCIAVI_mciUpdate (dwDevID, dwParam1, (LPMCI_DGV_UPDATE_PARMS) dwParam2); 1031 case MCI_WHERE: return MCIAVI_mciWhere (dwDevID, dwParam1, (LPMCI_DGV_RECT_PARMS) dwParam2); 1032 case MCI_STEP: return MCIAVI_mciStep (dwDevID, dwParam1, (LPMCI_DGV_STEP_PARMS) dwParam2); 1033 case MCI_CUE: return MCIAVI_mciCue (dwDevID, dwParam1, (LPMCI_DGV_CUE_PARMS) dwParam2); 1034 case MCI_BREAK: return MCIAVI_mciBreak (dwDevID, dwParam1, (LPMCI_BREAK_PARMS) dwParam2); 1035 /* Digital Video specific */ 1036 case MCI_SETAUDIO: return MCIAVI_mciSetAudio (dwDevID, dwParam1, (LPMCI_DGV_SETAUDIO_PARMSW) dwParam2); 1037 case MCI_SIGNAL: return MCIAVI_mciSignal (dwDevID, dwParam1, (LPMCI_DGV_SIGNAL_PARMS) dwParam2); 1038 case MCI_SETVIDEO: return MCIAVI_mciSetVideo (dwDevID, dwParam1, (LPMCI_DGV_SETVIDEO_PARMSW) dwParam2); 1039 case MCI_CONFIGURE: return MCIAVI_mciConfigure (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2); 1040 1041 /* no editing, recording, saving, locking without inputs */ 1042 case MCI_CAPTURE: 1043 case MCI_COPY: 1044 case MCI_CUT: 1045 case MCI_DELETE: 1046 case MCI_FREEZE: 1047 case MCI_LIST: 1048 case MCI_MONITOR: 1049 case MCI_PASTE: 1050 case MCI_QUALITY: 1051 case MCI_RECORD: 1052 case MCI_RESERVE: 1053 case MCI_RESTORE: 1054 case MCI_SAVE: 1055 case MCI_UNDO: 1056 case MCI_UNFREEZE: 1057 TRACE("Unsupported function [0x%x] flags=%08x\n", wMsg, (DWORD)dwParam1); 1058 return MCIERR_UNSUPPORTED_FUNCTION; 1059 case MCI_SPIN: 1060 case MCI_ESCAPE: 1061 WARN("Unsupported command [0x%x] %08x\n", wMsg, (DWORD)dwParam1); 1062 break; 1063 case MCI_OPEN: 1064 case MCI_CLOSE: 1065 FIXME("Shouldn't receive a MCI_OPEN or CLOSE message\n"); 1066 break; 1067 default: 1068 TRACE("Sending msg [%u] to default driver proc\n", wMsg); 1069 return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2); 1070 } 1071 return MCIERR_UNRECOGNIZED_COMMAND; 1072 } 1073