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