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