jscript: Don't pass 'this' argument to DISPID_VALUE of pure IDispatch interfaces.
[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->hStopEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
91     wma->wDevID = modp->wDeviceID;
92     wma->wCommandTable = mciLoadCommandResource(MCIAVI_hInstance, mciAviWStr, 0);
93     wma->dwStatus = MCI_MODE_NOT_READY;
94     modp->wCustomCommandTable = wma->wCommandTable;
95     modp->wType = MCI_DEVTYPE_DIGITAL_VIDEO;
96     mciSetDriverData(wma->wDevID, (DWORD_PTR)wma);
97
98     return modp->wDeviceID;
99 }
100
101 /**************************************************************************
102  *                              MCIAVI_drvClose         [internal]
103  */
104 static  DWORD   MCIAVI_drvClose(DWORD dwDevID)
105 {
106     WINE_MCIAVI *wma;
107
108     TRACE("%04x\n", dwDevID);
109
110     /* finish all outstanding things */
111     MCIAVI_mciClose(dwDevID, MCI_WAIT, NULL);
112
113     wma = (WINE_MCIAVI*)mciGetDriverData(dwDevID);
114
115     if (wma) {
116         MCIAVI_UnregisterClass();
117
118         EnterCriticalSection(&wma->cs);
119
120         mciSetDriverData(dwDevID, 0);
121         mciFreeCommandResource(wma->wCommandTable);
122
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             if (lpOpenParms->lpstrElementName[0] == '@') {
260                 /* The file name @11223344 encodes an AVIFile handle in decimal notation
261                  * in Win3.1 and w2k/NT, but this feature is absent in win95 (KB140750).
262                  * wma->hFile = LongToHandle(strtolW(lpOpenParms->lpstrElementName+1, NULL, 10)); */
263                 FIXME("Using AVIFile/Stream %s NIY\n", debugstr_w(lpOpenParms->lpstrElementName));
264             }
265             wma->hFile = mmioOpenW(lpOpenParms->lpstrElementName, NULL,
266                                    MMIO_ALLOCBUF | MMIO_DENYWRITE | MMIO_READ);
267
268             if (wma->hFile == 0) {
269                 WARN("can't find file=%s!\n", debugstr_w(lpOpenParms->lpstrElementName));
270                 dwRet = MCIERR_FILE_NOT_FOUND;
271             } else {
272                 if (!MCIAVI_GetInfo(wma))
273                     dwRet = MCIERR_INVALID_FILE;
274                 else if (!MCIAVI_OpenVideo(wma))
275                     dwRet = MCIERR_CANNOT_LOAD_DRIVER;
276                 else if (!MCIAVI_CreateWindow(wma, dwFlags, lpOpenParms))
277                     dwRet = MCIERR_CREATEWINDOW;
278             }
279         } else {
280             FIXME("Don't record yet\n");
281             dwRet = MCIERR_UNSUPPORTED_FUNCTION;
282         }
283     }
284
285     if (dwRet == 0) {
286         TRACE("lpOpenParms->wDeviceID = %04x\n", lpOpenParms->wDeviceID);
287
288         wma->dwStatus = MCI_MODE_STOP;
289         wma->dwMciTimeFormat = MCI_FORMAT_FRAMES;
290     } else {
291         MCIAVI_CleanUp(wma);
292     }
293
294     LeaveCriticalSection(&wma->cs);
295
296     if (!dwRet && (dwFlags & MCI_NOTIFY)) {
297         mciDriverNotify(HWND_32(LOWORD(lpOpenParms->dwCallback)),
298                        wDevID, MCI_NOTIFY_SUCCESSFUL);
299     }
300     return dwRet;
301 }
302
303 /***************************************************************************
304  *                              MCIAVI_mciClose                 [internal]
305  */
306 DWORD MCIAVI_mciClose(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
307 {
308     WINE_MCIAVI *wma;
309     DWORD               dwRet = 0;
310
311     TRACE("(%04x, %08X, %p)\n", wDevID, dwFlags, lpParms);
312
313     wma = MCIAVI_mciGetOpenDev(wDevID);
314     if (wma == NULL)    return MCIERR_INVALID_DEVICE_ID;
315
316     MCIAVI_mciStop(wDevID, MCI_WAIT, NULL);
317
318     EnterCriticalSection(&wma->cs);
319
320     if (wma->nUseCount == 1) {
321         MCIAVI_CleanUp(wma);
322
323         if ((dwFlags & MCI_NOTIFY) && lpParms) {
324             mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
325                            wDevID,
326                             MCI_NOTIFY_SUCCESSFUL);
327         }
328         LeaveCriticalSection(&wma->cs);
329         return dwRet;
330     }
331     wma->nUseCount--;
332
333     LeaveCriticalSection(&wma->cs);
334     return dwRet;
335 }
336
337 static double currenttime_us(void)
338 {
339     LARGE_INTEGER lc, lf;
340     QueryPerformanceCounter(&lc);
341     QueryPerformanceFrequency(&lf);
342     return (lc.QuadPart * 1000000) / lf.QuadPart;
343 }
344
345 /***************************************************************************
346  *                              MCIAVI_player                   [internal]
347  */
348 static  DWORD   MCIAVI_player(WINE_MCIAVI *wma, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
349 {
350     DWORD               dwRet;
351     LPWAVEHDR           waveHdr = NULL;
352     unsigned            i, nHdr = 0;
353     DWORD               numEvents = 1;
354     HANDLE              events[2];
355     double next_frame_us;
356
357     EnterCriticalSection(&wma->cs);
358
359     if (wma->dwToVideoFrame <= wma->dwCurrVideoFrame)
360     {
361         dwRet = 0;
362         goto mci_play_done;
363     }
364
365     events[0] = wma->hStopEvent;
366     if (wma->lpWaveFormat) {
367        if (MCIAVI_OpenAudio(wma, &nHdr, &waveHdr) != 0)
368         {
369             /* can't play audio */
370             HeapFree(GetProcessHeap(), 0, wma->lpWaveFormat);
371             wma->lpWaveFormat = NULL;
372         }
373        else
374        {
375             /* fill the queue with as many wave headers as possible */
376             MCIAVI_PlayAudioBlocks(wma, nHdr, waveHdr);
377             events[1] = wma->hEvent;
378             numEvents = 2;
379        }
380     }
381
382     next_frame_us = currenttime_us();
383     while (wma->dwStatus == MCI_MODE_PLAY)
384     {
385         HDC hDC;
386         double tc, delta;
387         DWORD ret;
388
389         tc = currenttime_us();
390
391         hDC = wma->hWndPaint ? GetDC(wma->hWndPaint) : 0;
392         if (hDC)
393         {
394             while(next_frame_us <= tc && wma->dwCurrVideoFrame < wma->dwToVideoFrame){
395                 double dur;
396                 ++wma->dwCurrVideoFrame;
397                 dur = MCIAVI_PaintFrame(wma, hDC);
398                 if(!dur)
399                     break;
400                 next_frame_us += dur;
401                 TRACE("next_frame: %f\n", next_frame_us);
402             }
403             ReleaseDC(wma->hWndPaint, hDC);
404         }
405         if(wma->dwCurrVideoFrame >= wma->dwToVideoFrame)
406             break;
407
408         if (wma->lpWaveFormat)
409             MCIAVI_PlayAudioBlocks(wma, nHdr, waveHdr);
410
411         tc = currenttime_us();
412         if(tc < next_frame_us)
413             delta = next_frame_us - tc;
414         else
415             delta = 0;
416
417         LeaveCriticalSection(&wma->cs);
418         ret = WaitForMultipleObjects(numEvents, events, FALSE, delta / 1000);
419         EnterCriticalSection(&wma->cs);
420         if (ret == WAIT_OBJECT_0 || wma->dwStatus != MCI_MODE_PLAY) break;
421     }
422
423     if (wma->lpWaveFormat) {
424        while (wma->dwEventCount != nHdr - 1)
425         {
426             LeaveCriticalSection(&wma->cs);
427             Sleep(100);
428             EnterCriticalSection(&wma->cs);
429         }
430
431         /* just to get rid of some race conditions between play, stop and pause */
432         LeaveCriticalSection(&wma->cs);
433         waveOutReset(wma->hWave);
434         EnterCriticalSection(&wma->cs);
435
436         for (i = 0; i < nHdr; i++)
437             waveOutUnprepareHeader(wma->hWave, &waveHdr[i], sizeof(WAVEHDR));
438     }
439
440     dwRet = 0;
441
442     if (wma->lpWaveFormat) {
443         HeapFree(GetProcessHeap(), 0, waveHdr);
444
445         if (wma->hWave) {
446             LeaveCriticalSection(&wma->cs);
447             waveOutClose(wma->hWave);
448             EnterCriticalSection(&wma->cs);
449             wma->hWave = 0;
450         }
451         CloseHandle(wma->hEvent);
452     }
453
454 mci_play_done:
455     wma->dwStatus = MCI_MODE_STOP;
456
457     if (dwFlags & MCI_NOTIFY) {
458         TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
459         mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
460                        wma->wDevID, MCI_NOTIFY_SUCCESSFUL);
461     }
462     LeaveCriticalSection(&wma->cs);
463     return dwRet;
464 }
465
466 struct MCIAVI_play_data
467 {
468     WINE_MCIAVI *wma;
469     DWORD flags;
470     MCI_PLAY_PARMS params; /* FIXME: notify via wma->hCallback like the other MCI drivers */
471 };
472
473 /*
474  * MCIAVI_mciPlay_thread
475  *
476  * FIXME: probably should use a common worker thread created at the driver
477  * load time and queue all async commands to it.
478  */
479 static DWORD WINAPI MCIAVI_mciPlay_thread(LPVOID arg)
480 {
481     struct MCIAVI_play_data *data = (struct MCIAVI_play_data *)arg;
482     DWORD ret;
483
484     TRACE("In thread before async play command (id %u, flags %08x)\n", data->wma->wDevID, data->flags);
485     ret = MCIAVI_player(data->wma, data->flags, &data->params);
486     TRACE("In thread after async play command (id %u, flags %08x)\n", data->wma->wDevID, data->flags);
487
488     HeapFree(GetProcessHeap(), 0, data);
489     return ret;
490 }
491
492 /*
493  * MCIAVI_mciPlay_async
494  */
495 static DWORD MCIAVI_mciPlay_async(WINE_MCIAVI *wma, DWORD dwFlags, LPMCI_PLAY_PARMS lpParams)
496 {
497     HANDLE handle;
498     struct MCIAVI_play_data *data = HeapAlloc(GetProcessHeap(), 0, sizeof(struct MCIAVI_play_data));
499
500     if (!data) return MCIERR_OUT_OF_MEMORY;
501
502     data->wma = wma;
503     data->flags = dwFlags;
504     if (dwFlags & MCI_NOTIFY)
505         data->params.dwCallback = lpParams->dwCallback;
506
507     if (!(handle = CreateThread(NULL, 0, MCIAVI_mciPlay_thread, data, 0, NULL)))
508     {
509         WARN("Couldn't create thread for async play, playing synchronously\n");
510         return MCIAVI_mciPlay_thread(data);
511     }
512     SetThreadPriority(handle, THREAD_PRIORITY_TIME_CRITICAL);
513     CloseHandle(handle);
514     return 0;
515 }
516
517 /***************************************************************************
518  *                              MCIAVI_mciPlay                  [internal]
519  */
520 static  DWORD   MCIAVI_mciPlay(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
521 {
522     WINE_MCIAVI *wma;
523     DWORD               dwRet;
524     DWORD               dwFromFrame, dwToFrame;
525
526     TRACE("(%04x, %08X, %p)\n", wDevID, dwFlags, lpParms);
527
528     if (lpParms == NULL)        return MCIERR_NULL_PARAMETER_BLOCK;
529
530     wma = MCIAVI_mciGetOpenDev(wDevID);
531     if (wma == NULL)            return MCIERR_INVALID_DEVICE_ID;
532     if (dwFlags & MCI_DGV_PLAY_REVERSE) return MCIERR_UNSUPPORTED_FUNCTION;
533     if (dwFlags & MCI_TEST)     return 0;
534
535     if (dwFlags & (MCI_DGV_PLAY_REPEAT|MCI_MCIAVI_PLAY_WINDOW|MCI_MCIAVI_PLAY_FULLSCREEN|MCI_MCIAVI_PLAY_FULLBY2))
536         FIXME("Unsupported flag %08x\n", dwFlags);
537
538     EnterCriticalSection(&wma->cs);
539
540     if (!wma->hFile)
541     {
542         LeaveCriticalSection(&wma->cs);
543         return MCIERR_FILE_NOT_FOUND;
544     }
545     if (!wma->hWndPaint)
546     {
547         LeaveCriticalSection(&wma->cs);
548         return MCIERR_NO_WINDOW;
549     }
550
551     dwFromFrame = wma->dwCurrVideoFrame;
552     dwToFrame = wma->dwPlayableVideoFrames - 1;
553
554     if (dwFlags & MCI_FROM) {
555         dwFromFrame = MCIAVI_ConvertTimeFormatToFrame(wma, lpParms->dwFrom);
556     }
557     if (dwFlags & MCI_TO) {
558         dwToFrame = MCIAVI_ConvertTimeFormatToFrame(wma, lpParms->dwTo);
559     }
560     if (dwToFrame >= wma->dwPlayableVideoFrames)
561         dwToFrame = wma->dwPlayableVideoFrames - 1;
562
563     TRACE("Playing from frame=%u to frame=%u\n", dwFromFrame, dwToFrame);
564
565     wma->dwCurrVideoFrame = dwFromFrame;
566     wma->dwToVideoFrame = dwToFrame;
567
568     LeaveCriticalSection(&wma->cs);
569
570     if (!(GetWindowLongW(wma->hWndPaint, GWL_STYLE) & WS_VISIBLE))
571         ShowWindow(wma->hWndPaint, SW_SHOWNA);
572
573     EnterCriticalSection(&wma->cs);
574
575     /* if already playing exit */
576     if (wma->dwStatus == MCI_MODE_PLAY)
577     {
578         LeaveCriticalSection(&wma->cs);
579         return 0;
580     }
581
582     wma->dwStatus = MCI_MODE_PLAY;
583
584     LeaveCriticalSection(&wma->cs);
585
586     if (dwFlags & MCI_WAIT)
587         return MCIAVI_player(wma, dwFlags, lpParms);
588
589     dwRet = MCIAVI_mciPlay_async(wma, dwFlags, lpParms);
590
591     if (dwRet) {
592         EnterCriticalSection(&wma->cs);
593         wma->dwStatus = MCI_MODE_STOP;
594         LeaveCriticalSection(&wma->cs);
595     }
596     return dwRet;
597 }
598
599 /***************************************************************************
600  *                              MCIAVI_mciStop                  [internal]
601  */
602 static  DWORD   MCIAVI_mciStop(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
603 {
604     WINE_MCIAVI *wma;
605     DWORD               dwRet = 0;
606
607     TRACE("(%04x, %08X, %p)\n", wDevID, dwFlags, lpParms);
608
609     wma = MCIAVI_mciGetOpenDev(wDevID);
610     if (wma == NULL)            return MCIERR_INVALID_DEVICE_ID;
611     if (dwFlags & MCI_TEST)     return 0;
612
613     EnterCriticalSection(&wma->cs);
614
615     TRACE("current status %04x\n", wma->dwStatus);
616
617     switch (wma->dwStatus) {
618     case MCI_MODE_PLAY:
619     case MCI_MODE_RECORD:
620         LeaveCriticalSection(&wma->cs);
621         SetEvent(wma->hStopEvent);
622         EnterCriticalSection(&wma->cs);
623         /* fall through */
624     case MCI_MODE_PAUSE:
625         /* Since our wave notification callback takes the lock,
626          * we must release it before resetting the device */
627         LeaveCriticalSection(&wma->cs);
628         dwRet = waveOutReset(wma->hWave);
629         EnterCriticalSection(&wma->cs);
630         /* fall through */
631     default:
632         do /* one more chance for an async thread to finish */
633         {
634             LeaveCriticalSection(&wma->cs);
635             Sleep(10);
636             EnterCriticalSection(&wma->cs);
637         } while (wma->dwStatus != MCI_MODE_STOP);
638
639         break;
640
641     case MCI_MODE_NOT_READY:
642         break;        
643     }
644
645     if ((dwFlags & MCI_NOTIFY) && lpParms) {
646         mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
647                        wDevID, MCI_NOTIFY_SUCCESSFUL);
648     }
649     LeaveCriticalSection(&wma->cs);
650     return dwRet;
651 }
652
653 /***************************************************************************
654  *                              MCIAVI_mciPause                 [internal]
655  */
656 static  DWORD   MCIAVI_mciPause(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
657 {
658     WINE_MCIAVI *wma;
659
660     TRACE("(%04x, %08X, %p)\n", wDevID, dwFlags, lpParms);
661
662     wma = MCIAVI_mciGetOpenDev(wDevID);
663     if (wma == NULL)            return MCIERR_INVALID_DEVICE_ID;
664     if (dwFlags & MCI_TEST)     return 0;
665
666     EnterCriticalSection(&wma->cs);
667
668     if (wma->dwStatus == MCI_MODE_PLAY)
669         wma->dwStatus = MCI_MODE_PAUSE;
670
671     if (wma->lpWaveFormat) {
672         LeaveCriticalSection(&wma->cs);
673         return waveOutPause(wma->hWave);
674     }
675
676     LeaveCriticalSection(&wma->cs);
677     return 0;
678 }
679
680 /***************************************************************************
681  *                              MCIAVI_mciResume                        [internal]
682  */
683 static  DWORD   MCIAVI_mciResume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
684 {
685     WINE_MCIAVI *wma;
686
687     TRACE("(%04x, %08X, %p)\n", wDevID, dwFlags, lpParms);
688
689     wma = MCIAVI_mciGetOpenDev(wDevID);
690     if (wma == NULL)            return MCIERR_INVALID_DEVICE_ID;
691     if (dwFlags & MCI_TEST)     return 0;
692
693     EnterCriticalSection(&wma->cs);
694
695     if (wma->dwStatus == MCI_MODE_PAUSE)
696         wma->dwStatus = MCI_MODE_PLAY;
697
698     if (wma->lpWaveFormat) {
699         LeaveCriticalSection(&wma->cs);
700         return waveOutRestart(wma->hWave);
701     }
702
703     LeaveCriticalSection(&wma->cs);
704     return 0;
705 }
706
707 /***************************************************************************
708  *                              MCIAVI_mciSeek                  [internal]
709  */
710 static  DWORD   MCIAVI_mciSeek(UINT wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
711 {
712     WINE_MCIAVI *wma;
713     DWORD position;
714
715     TRACE("(%04x, %08X, %p)\n", wDevID, dwFlags, lpParms);
716
717     if (lpParms == NULL)        return MCIERR_NULL_PARAMETER_BLOCK;
718
719     wma = MCIAVI_mciGetOpenDev(wDevID);
720     if (wma == NULL)            return MCIERR_INVALID_DEVICE_ID;
721
722     position = dwFlags & (MCI_SEEK_TO_START|MCI_SEEK_TO_END|MCI_TO);
723     if (!position)              return MCIERR_MISSING_PARAMETER;
724     if (position&(position-1))  return MCIERR_FLAGS_NOT_COMPATIBLE;
725
726     if (dwFlags & MCI_TO) {
727         position = MCIAVI_ConvertTimeFormatToFrame(wma, lpParms->dwTo);
728         if (position >= wma->dwPlayableVideoFrames)
729             return MCIERR_OUTOFRANGE;
730     } else if (dwFlags & MCI_SEEK_TO_START) {
731         position = 0;
732     } else {
733         position = wma->dwPlayableVideoFrames - 1;
734     }
735     if (dwFlags & MCI_TEST)     return 0;
736
737     MCIAVI_mciStop(wDevID, MCI_WAIT, NULL);
738
739     EnterCriticalSection(&wma->cs);
740
741     wma->dwCurrVideoFrame = position;
742     TRACE("Seeking to frame=%u\n", wma->dwCurrVideoFrame);
743
744     if (dwFlags & MCI_NOTIFY) {
745         mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
746                        wDevID, MCI_NOTIFY_SUCCESSFUL);
747     }
748     LeaveCriticalSection(&wma->cs);
749     return 0;
750 }
751
752 /*****************************************************************************
753  *                              MCIAVI_mciLoad                  [internal]
754  */
755 static DWORD    MCIAVI_mciLoad(UINT wDevID, DWORD dwFlags, LPMCI_DGV_LOAD_PARMSW lpParms)
756 {
757     WINE_MCIAVI *wma;
758
759     FIXME("(%04x, %08x, %p) : stub\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
766     return MCIERR_UNSUPPORTED_FUNCTION; /* like w2k */
767 }
768
769 /******************************************************************************
770  *                              MCIAVI_mciRealize                       [internal]
771  */
772 static  DWORD   MCIAVI_mciRealize(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
773 {
774     WINE_MCIAVI *wma;
775
776     FIXME("(%04x, %08x, %p) : stub\n", wDevID, dwFlags, lpParms);
777
778     if (lpParms == NULL)        return MCIERR_NULL_PARAMETER_BLOCK;
779
780     wma = MCIAVI_mciGetOpenDev(wDevID);
781     if (wma == NULL)            return MCIERR_INVALID_DEVICE_ID;
782     if (dwFlags & MCI_TEST)     return 0;
783
784     return 0;
785 }
786
787 /******************************************************************************
788  *                              MCIAVI_mciUpdate                        [internal]
789  */
790 static  DWORD   MCIAVI_mciUpdate(UINT wDevID, DWORD dwFlags, LPMCI_DGV_UPDATE_PARMS lpParms)
791 {
792     WINE_MCIAVI *wma;
793
794     TRACE("%04x, %08x, %p\n", wDevID, dwFlags, lpParms);
795
796     if (lpParms == NULL)        return MCIERR_NULL_PARAMETER_BLOCK;
797
798     wma = MCIAVI_mciGetOpenDev(wDevID);
799     if (wma == NULL)            return MCIERR_INVALID_DEVICE_ID;
800     /* Ignore MCI_TEST flag. */
801
802     EnterCriticalSection(&wma->cs);
803
804     if (dwFlags & MCI_DGV_UPDATE_HDC)
805         MCIAVI_PaintFrame(wma, lpParms->hDC);
806
807     LeaveCriticalSection(&wma->cs);
808
809     return 0;
810 }
811
812 /******************************************************************************
813  *                              MCIAVI_mciStep                  [internal]
814  */
815 static  DWORD   MCIAVI_mciStep(UINT wDevID, DWORD dwFlags, LPMCI_DGV_STEP_PARMS lpParms)
816 {
817     WINE_MCIAVI *wma;
818     DWORD position;
819     int delta = 1;
820
821     TRACE("(%04x, %08x, %p)\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
828     if (dwFlags & MCI_DGV_STEP_FRAMES)  delta = lpParms->dwFrames;
829     if (dwFlags & MCI_DGV_STEP_REVERSE) delta = -delta;
830     position = wma->dwCurrVideoFrame + delta;
831     if (position >= wma->dwPlayableVideoFrames) return MCIERR_OUTOFRANGE;
832     if (dwFlags & MCI_TEST)     return 0;
833
834     MCIAVI_mciStop(wDevID, MCI_WAIT, NULL);
835
836     EnterCriticalSection(&wma->cs);
837
838     wma->dwCurrVideoFrame = position;
839     TRACE("Stepping to frame=%u\n", wma->dwCurrVideoFrame);
840
841     if (dwFlags & MCI_NOTIFY) {
842         mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
843                        wDevID, MCI_NOTIFY_SUCCESSFUL);
844     }
845     LeaveCriticalSection(&wma->cs);
846     return 0;
847 }
848
849 /******************************************************************************
850  *                              MCIAVI_mciCue                   [internal]
851  */
852 static  DWORD   MCIAVI_mciCue(UINT wDevID, DWORD dwFlags, LPMCI_DGV_CUE_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     if (dwFlags & MCI_DGV_CUE_INPUT) return MCIERR_UNSUPPORTED_FUNCTION;
863     if (dwFlags & MCI_TEST)     return 0;
864
865     return 0;
866 }
867
868 /******************************************************************************
869  *                              MCIAVI_mciSetAudio                      [internal]
870  */
871 static  DWORD   MCIAVI_mciSetAudio(UINT wDevID, DWORD dwFlags, LPMCI_DGV_SETAUDIO_PARMSW lpParms)
872 {
873     WINE_MCIAVI *wma;
874
875     if (lpParms == NULL)        return MCIERR_NULL_PARAMETER_BLOCK;
876
877     FIXME("(%04x, %08x, %p) Item %04x: stub\n", wDevID, dwFlags, lpParms, dwFlags & MCI_DGV_SETAUDIO_ITEM ? lpParms->dwItem : 0);
878
879     wma = MCIAVI_mciGetOpenDev(wDevID);
880     if (wma == NULL)            return MCIERR_INVALID_DEVICE_ID;
881
882     return 0;
883 }
884
885 /******************************************************************************
886  *                              MCIAVI_mciSignal                        [internal]
887  */
888 static  DWORD   MCIAVI_mciSignal(UINT wDevID, DWORD dwFlags, LPMCI_DGV_SIGNAL_PARMS lpParms)
889 {
890     WINE_MCIAVI *wma;
891
892     FIXME("(%04x, %08x, %p) : stub\n", wDevID, dwFlags, lpParms);
893
894     if (lpParms == NULL)        return MCIERR_NULL_PARAMETER_BLOCK;
895
896     wma = MCIAVI_mciGetOpenDev(wDevID);
897     if (wma == NULL)            return MCIERR_INVALID_DEVICE_ID;
898
899     return 0;
900 }
901
902 /******************************************************************************
903  *                              MCIAVI_mciSetVideo                      [internal]
904  */
905 static  DWORD   MCIAVI_mciSetVideo(UINT wDevID, DWORD dwFlags, LPMCI_DGV_SETVIDEO_PARMSW lpParms)
906 {
907     WINE_MCIAVI *wma;
908
909     if (lpParms == NULL)        return MCIERR_NULL_PARAMETER_BLOCK;
910
911     FIXME("(%04x, %08x, %p) Item %04x: stub\n", wDevID, dwFlags, lpParms, dwFlags & MCI_DGV_SETVIDEO_ITEM ? lpParms->dwItem : 0);
912
913     wma = MCIAVI_mciGetOpenDev(wDevID);
914     if (wma == NULL)            return MCIERR_INVALID_DEVICE_ID;
915
916     return 0;
917 }
918
919 /******************************************************************************
920  *                              MCIAVI_mciConfigure                     [internal]
921  */
922 static  DWORD   MCIAVI_mciConfigure(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
923 {
924     WINE_MCIAVI *wma;
925
926     FIXME("(%04x, %08x, %p) : stub\n", wDevID, dwFlags, lpParms);
927
928     if (lpParms == NULL)        return MCIERR_NULL_PARAMETER_BLOCK;
929
930     wma = MCIAVI_mciGetOpenDev(wDevID);
931     if (wma == NULL)            return MCIERR_INVALID_DEVICE_ID;
932     if (dwFlags & MCI_TEST)     return 0;
933
934     return 0;
935 }
936
937 /*======================================================================*
938  *                          MCI AVI entry points                        *
939  *======================================================================*/
940
941 /**************************************************************************
942  *                              DriverProc (MCIAVI.@)
943  */
944 LRESULT CALLBACK MCIAVI_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg,
945                                    LPARAM dwParam1, LPARAM dwParam2)
946 {
947     TRACE("(%08lX, %p, %08X, %08lX, %08lX)\n",
948           dwDevID, hDriv, wMsg, dwParam1, dwParam2);
949
950     switch (wMsg) {
951     case DRV_LOAD:              return 1;
952     case DRV_FREE:              return 1;
953     case DRV_OPEN:              return MCIAVI_drvOpen((LPCWSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSW)dwParam2);
954     case DRV_CLOSE:             return MCIAVI_drvClose(dwDevID);
955     case DRV_ENABLE:            return 1;
956     case DRV_DISABLE:           return 1;
957     case DRV_QUERYCONFIGURE:    return 1;
958     case DRV_CONFIGURE:         return MCIAVI_drvConfigure(dwDevID);
959     case DRV_INSTALL:           return DRVCNF_RESTART;
960     case DRV_REMOVE:            return DRVCNF_RESTART;
961     }
962
963     /* session instance */
964     if (dwDevID == 0xFFFFFFFF) return 1;
965
966     switch (wMsg) {
967     case MCI_OPEN_DRIVER:       return MCIAVI_mciOpen      (dwDevID, dwParam1, (LPMCI_DGV_OPEN_PARMSW)     dwParam2);
968     case MCI_CLOSE_DRIVER:      return MCIAVI_mciClose     (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)       dwParam2);
969     case MCI_PLAY:              return MCIAVI_mciPlay      (dwDevID, dwParam1, (LPMCI_PLAY_PARMS)          dwParam2);
970     case MCI_STOP:              return MCIAVI_mciStop      (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)       dwParam2);
971     case MCI_SET:               return MCIAVI_mciSet       (dwDevID, dwParam1, (LPMCI_DGV_SET_PARMS)       dwParam2);
972     case MCI_PAUSE:             return MCIAVI_mciPause     (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)       dwParam2);
973     case MCI_RESUME:            return MCIAVI_mciResume    (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)       dwParam2);
974     case MCI_STATUS:            return MCIAVI_mciStatus    (dwDevID, dwParam1, (LPMCI_DGV_STATUS_PARMSW)   dwParam2);
975     case MCI_GETDEVCAPS:        return MCIAVI_mciGetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS)    dwParam2);
976     case MCI_INFO:              return MCIAVI_mciInfo      (dwDevID, dwParam1, (LPMCI_DGV_INFO_PARMSW)     dwParam2);
977     case MCI_SEEK:              return MCIAVI_mciSeek      (dwDevID, dwParam1, (LPMCI_SEEK_PARMS)          dwParam2);
978     case MCI_PUT:               return MCIAVI_mciPut       (dwDevID, dwParam1, (LPMCI_DGV_PUT_PARMS)       dwParam2);
979     case MCI_WINDOW:            return MCIAVI_mciWindow    (dwDevID, dwParam1, (LPMCI_DGV_WINDOW_PARMSW)   dwParam2);
980     case MCI_LOAD:              return MCIAVI_mciLoad      (dwDevID, dwParam1, (LPMCI_DGV_LOAD_PARMSW)     dwParam2);
981     case MCI_REALIZE:           return MCIAVI_mciRealize   (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)       dwParam2);
982     case MCI_UPDATE:            return MCIAVI_mciUpdate    (dwDevID, dwParam1, (LPMCI_DGV_UPDATE_PARMS)    dwParam2);
983     case MCI_WHERE:             return MCIAVI_mciWhere     (dwDevID, dwParam1, (LPMCI_DGV_RECT_PARMS)      dwParam2);
984     case MCI_STEP:              return MCIAVI_mciStep      (dwDevID, dwParam1, (LPMCI_DGV_STEP_PARMS)      dwParam2);
985     case MCI_CUE:               return MCIAVI_mciCue       (dwDevID, dwParam1, (LPMCI_DGV_CUE_PARMS)       dwParam2);
986         /* Digital Video specific */
987     case MCI_SETAUDIO:          return MCIAVI_mciSetAudio  (dwDevID, dwParam1, (LPMCI_DGV_SETAUDIO_PARMSW) dwParam2);
988     case MCI_SIGNAL:            return MCIAVI_mciSignal    (dwDevID, dwParam1, (LPMCI_DGV_SIGNAL_PARMS)    dwParam2);
989     case MCI_SETVIDEO:          return MCIAVI_mciSetVideo  (dwDevID, dwParam1, (LPMCI_DGV_SETVIDEO_PARMSW) dwParam2);
990     case MCI_CONFIGURE:         return MCIAVI_mciConfigure (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)       dwParam2);
991
992         /* no editing, recording, saving, locking without inputs */
993     case MCI_CAPTURE:
994     case MCI_COPY:
995     case MCI_CUT:
996     case MCI_DELETE:
997     case MCI_FREEZE:
998     case MCI_LIST:
999     case MCI_MONITOR:
1000     case MCI_PASTE:
1001     case MCI_QUALITY:
1002     case MCI_RECORD:
1003     case MCI_RESERVE:
1004     case MCI_RESTORE:
1005     case MCI_SAVE:
1006     case MCI_UNDO:
1007     case MCI_UNFREEZE:
1008         TRACE("Unsupported function [0x%x] flags=%08x\n", wMsg, (DWORD)dwParam1);
1009         return MCIERR_UNSUPPORTED_FUNCTION;
1010     case MCI_SPIN:
1011     case MCI_ESCAPE:
1012         WARN("Unsupported command [0x%x] %08x\n", wMsg, (DWORD)dwParam1);
1013         break;
1014     case MCI_OPEN:
1015     case MCI_CLOSE:
1016         FIXME("Shouldn't receive a MCI_OPEN or CLOSE message\n");
1017         break;
1018     default:
1019         TRACE("Sending msg [%u] to default driver proc\n", wMsg);
1020         return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1021     }
1022     return MCIERR_UNRECOGNIZED_COMMAND;
1023 }