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