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