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