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