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