xref: /reactos/dll/win32/mciavi32/mciavi.c (revision bae2bac6)
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     BOOL wait_audio = TRUE;
357 
358     EnterCriticalSection(&wma->cs);
359 
360     if (wma->dwToVideoFrame <= wma->dwCurrVideoFrame)
361     {
362         dwRet = 0;
363         goto mci_play_done;
364     }
365 
366     events[0] = wma->hStopEvent;
367     if (wma->lpWaveFormat) {
368        if (MCIAVI_OpenAudio(wma, &nHdr, &waveHdr) != 0)
369         {
370             /* can't play audio */
371             HeapFree(GetProcessHeap(), 0, wma->lpWaveFormat);
372             wma->lpWaveFormat = NULL;
373         }
374        else
375        {
376             /* fill the queue with as many wave headers as possible */
377             MCIAVI_PlayAudioBlocks(wma, nHdr, waveHdr);
378             events[1] = wma->hEvent;
379             numEvents = 2;
380        }
381     }
382 
383     next_frame_us = currenttime_us();
384     while (wma->dwStatus == MCI_MODE_PLAY)
385     {
386         HDC hDC;
387         double tc, delta;
388         DWORD ret;
389 
390         tc = currenttime_us();
391 
392         hDC = wma->hWndPaint ? GetDC(wma->hWndPaint) : 0;
393         if (hDC)
394         {
395             while(next_frame_us <= tc && wma->dwCurrVideoFrame < wma->dwToVideoFrame){
396                 double dur;
397                 dur = MCIAVI_PaintFrame(wma, hDC);
398                 ++wma->dwCurrVideoFrame;
399                 if(!dur)
400                     break;
401                 next_frame_us += dur;
402                 TRACE("next_frame: %f\n", next_frame_us);
403             }
404             ReleaseDC(wma->hWndPaint, hDC);
405         }
406         if (wma->dwCurrVideoFrame >= wma->dwToVideoFrame)
407         {
408             if (!(dwFlags & MCI_DGV_PLAY_REPEAT))
409                 break;
410             TRACE("repeat media as requested\n");
411             wma->dwCurrVideoFrame = wma->dwCurrAudioBlock = 0;
412         }
413 
414         if (wma->lpWaveFormat)
415             MCIAVI_PlayAudioBlocks(wma, nHdr, waveHdr);
416 
417         tc = currenttime_us();
418         if (tc < next_frame_us)
419             delta = next_frame_us - tc;
420         else
421             delta = 0;
422 
423         /* check if the playback was cancelled */
424         if ((wma->mci_break.flags & MCI_BREAK_KEY) &&
425             (GetAsyncKeyState(wma->mci_break.parms.nVirtKey) & 0x8000))
426         {
427             if (!(wma->mci_break.flags & MCI_BREAK_HWND) ||
428                 GetForegroundWindow() == wma->mci_break.parms.hwndBreak)
429             {
430                 /* we queue audio blocks ahead so ignore them otherwise the audio
431                  * will keep playing until the buffer is empty */
432                 wait_audio = FALSE;
433 
434                 TRACE("playback cancelled using break key\n");
435                 break;
436             }
437         }
438 
439         LeaveCriticalSection(&wma->cs);
440         ret = WaitForMultipleObjects(numEvents, events, FALSE, delta / 1000);
441         EnterCriticalSection(&wma->cs);
442         if (ret == WAIT_OBJECT_0 || wma->dwStatus != MCI_MODE_PLAY) break;
443     }
444 
445     if (wma->lpWaveFormat)
446     {
447         if (wait_audio)
448             while (wma->dwEventCount != nHdr - 1)
449             {
450                 LeaveCriticalSection(&wma->cs);
451                 Sleep(100);
452                 EnterCriticalSection(&wma->cs);
453             }
454 
455 	/* just to get rid of some race conditions between play, stop and pause */
456 	LeaveCriticalSection(&wma->cs);
457 	waveOutReset(wma->hWave);
458 	EnterCriticalSection(&wma->cs);
459 
460 	for (i = 0; i < nHdr; i++)
461 	    waveOutUnprepareHeader(wma->hWave, &waveHdr[i], sizeof(WAVEHDR));
462     }
463 
464     dwRet = 0;
465 
466     if (wma->lpWaveFormat) {
467 	HeapFree(GetProcessHeap(), 0, waveHdr);
468 
469 	if (wma->hWave) {
470 	    LeaveCriticalSection(&wma->cs);
471 	    waveOutClose(wma->hWave);
472 	    EnterCriticalSection(&wma->cs);
473 	    wma->hWave = 0;
474 	}
475 	CloseHandle(wma->hEvent);
476     }
477 
478 mci_play_done:
479     wma->dwStatus = MCI_MODE_STOP;
480 
481     if (dwFlags & MCI_NOTIFY) {
482 	TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
483 	mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
484                        wma->wDevID, MCI_NOTIFY_SUCCESSFUL);
485     }
486     LeaveCriticalSection(&wma->cs);
487     return dwRet;
488 }
489 
490 struct MCIAVI_play_data
491 {
492     WINE_MCIAVI *wma;
493     DWORD flags;
494     MCI_PLAY_PARMS params; /* FIXME: notify via wma->hCallback like the other MCI drivers */
495 };
496 
497 /*
498  * MCIAVI_mciPlay_thread
499  *
500  * FIXME: probably should use a common worker thread created at the driver
501  * load time and queue all async commands to it.
502  */
503 static DWORD WINAPI MCIAVI_mciPlay_thread(LPVOID arg)
504 {
505     struct MCIAVI_play_data *data = (struct MCIAVI_play_data *)arg;
506     DWORD ret;
507 
508     TRACE("In thread before async play command (id %u, flags %08x)\n", data->wma->wDevID, data->flags);
509     ret = MCIAVI_player(data->wma, data->flags, &data->params);
510     TRACE("In thread after async play command (id %u, flags %08x)\n", data->wma->wDevID, data->flags);
511 
512     HeapFree(GetProcessHeap(), 0, data);
513     return ret;
514 }
515 
516 /*
517  * MCIAVI_mciPlay_async
518  */
519 static DWORD MCIAVI_mciPlay_async(WINE_MCIAVI *wma, DWORD dwFlags, LPMCI_PLAY_PARMS lpParams)
520 {
521     HANDLE handle;
522     struct MCIAVI_play_data *data = HeapAlloc(GetProcessHeap(), 0, sizeof(struct MCIAVI_play_data));
523 
524     if (!data) return MCIERR_OUT_OF_MEMORY;
525 
526     data->wma = wma;
527     data->flags = dwFlags;
528     if (dwFlags & MCI_NOTIFY)
529         data->params.dwCallback = lpParams->dwCallback;
530 
531     if (!(handle = CreateThread(NULL, 0, MCIAVI_mciPlay_thread, data, 0, NULL)))
532     {
533         WARN("Couldn't create thread for async play, playing synchronously\n");
534         return MCIAVI_mciPlay_thread(data);
535     }
536     SetThreadPriority(handle, THREAD_PRIORITY_TIME_CRITICAL);
537     CloseHandle(handle);
538     return 0;
539 }
540 
541 /***************************************************************************
542  * 				MCIAVI_mciPlay			[internal]
543  */
544 static	DWORD	MCIAVI_mciPlay(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
545 {
546     WINE_MCIAVI *wma;
547     DWORD		dwRet;
548     DWORD		dwFromFrame, dwToFrame;
549 
550     TRACE("(%04x, %08X, %p)\n", wDevID, dwFlags, lpParms);
551 
552     if (lpParms == NULL)	return MCIERR_NULL_PARAMETER_BLOCK;
553 
554     wma = MCIAVI_mciGetOpenDev(wDevID);
555     if (wma == NULL)		return MCIERR_INVALID_DEVICE_ID;
556     if (dwFlags & MCI_DGV_PLAY_REVERSE) return MCIERR_UNSUPPORTED_FUNCTION;
557     if (dwFlags & MCI_TEST)	return 0;
558 
559     if (dwFlags & (MCI_MCIAVI_PLAY_WINDOW|MCI_MCIAVI_PLAY_FULLSCREEN|MCI_MCIAVI_PLAY_FULLBY2))
560 	FIXME("Unsupported flag %08x\n", dwFlags);
561 
562     EnterCriticalSection(&wma->cs);
563 
564     if (!wma->hFile)
565     {
566         LeaveCriticalSection(&wma->cs);
567         return MCIERR_FILE_NOT_FOUND;
568     }
569     if (!wma->hWndPaint)
570     {
571         LeaveCriticalSection(&wma->cs);
572         return MCIERR_NO_WINDOW;
573     }
574 
575     dwFromFrame = wma->dwCurrVideoFrame;
576     dwToFrame = wma->dwPlayableVideoFrames - 1;
577 
578     if (dwFlags & MCI_FROM) {
579 	dwFromFrame = MCIAVI_ConvertTimeFormatToFrame(wma, lpParms->dwFrom);
580     }
581     if (dwFlags & MCI_TO) {
582 	dwToFrame = MCIAVI_ConvertTimeFormatToFrame(wma, lpParms->dwTo);
583     }
584     if (dwToFrame >= wma->dwPlayableVideoFrames)
585 	dwToFrame = wma->dwPlayableVideoFrames - 1;
586 
587     TRACE("Playing from frame=%u to frame=%u\n", dwFromFrame, dwToFrame);
588 
589     wma->dwCurrVideoFrame = dwFromFrame;
590     wma->dwToVideoFrame = dwToFrame;
591 
592     LeaveCriticalSection(&wma->cs);
593 
594     if (!(GetWindowLongW(wma->hWndPaint, GWL_STYLE) & WS_VISIBLE))
595         ShowWindow(wma->hWndPaint, SW_SHOWNA);
596 
597     EnterCriticalSection(&wma->cs);
598 
599     /* if already playing exit */
600     if (wma->dwStatus == MCI_MODE_PLAY)
601     {
602         LeaveCriticalSection(&wma->cs);
603         return 0;
604     }
605 
606     wma->dwStatus = MCI_MODE_PLAY;
607 
608     LeaveCriticalSection(&wma->cs);
609 
610     if (dwFlags & MCI_WAIT)
611         return MCIAVI_player(wma, dwFlags, lpParms);
612 
613     dwRet = MCIAVI_mciPlay_async(wma, dwFlags, lpParms);
614 
615     if (dwRet) {
616         EnterCriticalSection(&wma->cs);
617         wma->dwStatus = MCI_MODE_STOP;
618         LeaveCriticalSection(&wma->cs);
619     }
620     return dwRet;
621 }
622 
623 /***************************************************************************
624  * 				MCIAVI_mciStop			[internal]
625  */
626 static	DWORD	MCIAVI_mciStop(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
627 {
628     WINE_MCIAVI *wma;
629     DWORD		dwRet = 0;
630 
631     TRACE("(%04x, %08X, %p)\n", wDevID, dwFlags, lpParms);
632 
633     wma = MCIAVI_mciGetOpenDev(wDevID);
634     if (wma == NULL)		return MCIERR_INVALID_DEVICE_ID;
635     if (dwFlags & MCI_TEST)	return 0;
636 
637     EnterCriticalSection(&wma->cs);
638 
639     TRACE("current status %04x\n", wma->dwStatus);
640 
641     switch (wma->dwStatus) {
642     case MCI_MODE_PLAY:
643     case MCI_MODE_RECORD:
644         LeaveCriticalSection(&wma->cs);
645         SetEvent(wma->hStopEvent);
646         EnterCriticalSection(&wma->cs);
647         /* fall through */
648     case MCI_MODE_PAUSE:
649 	/* Since our wave notification callback takes the lock,
650 	 * we must release it before resetting the device */
651         LeaveCriticalSection(&wma->cs);
652         dwRet = waveOutReset(wma->hWave);
653         EnterCriticalSection(&wma->cs);
654         /* fall through */
655     default:
656         do /* one more chance for an async thread to finish */
657         {
658             LeaveCriticalSection(&wma->cs);
659             Sleep(10);
660             EnterCriticalSection(&wma->cs);
661         } while (wma->dwStatus != MCI_MODE_STOP);
662 
663 	break;
664 
665     case MCI_MODE_NOT_READY:
666         break;
667     }
668 
669     if ((dwFlags & MCI_NOTIFY) && lpParms) {
670 	mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
671                        wDevID, MCI_NOTIFY_SUCCESSFUL);
672     }
673     LeaveCriticalSection(&wma->cs);
674     return dwRet;
675 }
676 
677 /***************************************************************************
678  * 				MCIAVI_mciPause			[internal]
679  */
680 static	DWORD	MCIAVI_mciPause(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
681 {
682     WINE_MCIAVI *wma;
683 
684     TRACE("(%04x, %08X, %p)\n", wDevID, dwFlags, lpParms);
685 
686     wma = MCIAVI_mciGetOpenDev(wDevID);
687     if (wma == NULL)		return MCIERR_INVALID_DEVICE_ID;
688     if (dwFlags & MCI_TEST)	return 0;
689 
690     EnterCriticalSection(&wma->cs);
691 
692     if (wma->dwStatus == MCI_MODE_PLAY)
693 	wma->dwStatus = MCI_MODE_PAUSE;
694 
695     if (wma->lpWaveFormat) {
696     	LeaveCriticalSection(&wma->cs);
697 	return waveOutPause(wma->hWave);
698     }
699 
700     LeaveCriticalSection(&wma->cs);
701     return 0;
702 }
703 
704 /***************************************************************************
705  * 				MCIAVI_mciResume			[internal]
706  */
707 static	DWORD	MCIAVI_mciResume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
708 {
709     WINE_MCIAVI *wma;
710 
711     TRACE("(%04x, %08X, %p)\n", wDevID, dwFlags, lpParms);
712 
713     wma = MCIAVI_mciGetOpenDev(wDevID);
714     if (wma == NULL)		return MCIERR_INVALID_DEVICE_ID;
715     if (dwFlags & MCI_TEST)	return 0;
716 
717     EnterCriticalSection(&wma->cs);
718 
719     if (wma->dwStatus == MCI_MODE_PAUSE)
720 	wma->dwStatus = MCI_MODE_PLAY;
721 
722     if (wma->lpWaveFormat) {
723     	LeaveCriticalSection(&wma->cs);
724 	return waveOutRestart(wma->hWave);
725     }
726 
727     LeaveCriticalSection(&wma->cs);
728     return 0;
729 }
730 
731 /***************************************************************************
732  * 				MCIAVI_mciSeek			[internal]
733  */
734 static	DWORD	MCIAVI_mciSeek(UINT wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
735 {
736     WINE_MCIAVI *wma;
737     DWORD position;
738 
739     TRACE("(%04x, %08X, %p)\n", wDevID, dwFlags, lpParms);
740 
741     if (lpParms == NULL)	return MCIERR_NULL_PARAMETER_BLOCK;
742 
743     wma = MCIAVI_mciGetOpenDev(wDevID);
744     if (wma == NULL)		return MCIERR_INVALID_DEVICE_ID;
745 
746     position = dwFlags & (MCI_SEEK_TO_START|MCI_SEEK_TO_END|MCI_TO);
747     if (!position)		return MCIERR_MISSING_PARAMETER;
748     if (position&(position-1))	return MCIERR_FLAGS_NOT_COMPATIBLE;
749 
750     if (dwFlags & MCI_TO) {
751 	position = MCIAVI_ConvertTimeFormatToFrame(wma, lpParms->dwTo);
752 	if (position >= wma->dwPlayableVideoFrames)
753 	    return MCIERR_OUTOFRANGE;
754     } else if (dwFlags & MCI_SEEK_TO_START) {
755 	position = 0;
756     } else {
757 	position = wma->dwPlayableVideoFrames - 1;
758     }
759     if (dwFlags & MCI_TEST)	return 0;
760 
761     MCIAVI_mciStop(wDevID, MCI_WAIT, NULL);
762 
763     EnterCriticalSection(&wma->cs);
764 
765     wma->dwCurrVideoFrame = position;
766     TRACE("Seeking to frame=%u\n", wma->dwCurrVideoFrame);
767 
768     if (dwFlags & MCI_NOTIFY) {
769 	mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
770                        wDevID, MCI_NOTIFY_SUCCESSFUL);
771     }
772     LeaveCriticalSection(&wma->cs);
773     return 0;
774 }
775 
776 /*****************************************************************************
777  * 				MCIAVI_mciLoad			[internal]
778  */
779 static DWORD	MCIAVI_mciLoad(UINT wDevID, DWORD dwFlags, LPMCI_DGV_LOAD_PARMSW lpParms)
780 {
781     WINE_MCIAVI *wma;
782 
783     FIXME("(%04x, %08x, %p) : stub\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 
790     return MCIERR_UNSUPPORTED_FUNCTION; /* like w2k */
791 }
792 
793 /******************************************************************************
794  * 				MCIAVI_mciRealize			[internal]
795  */
796 static	DWORD	MCIAVI_mciRealize(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
797 {
798     WINE_MCIAVI *wma;
799 
800     FIXME("(%04x, %08x, %p) : stub\n", wDevID, dwFlags, lpParms);
801 
802     if (lpParms == NULL)	return MCIERR_NULL_PARAMETER_BLOCK;
803 
804     wma = MCIAVI_mciGetOpenDev(wDevID);
805     if (wma == NULL)		return MCIERR_INVALID_DEVICE_ID;
806     if (dwFlags & MCI_TEST)	return 0;
807 
808     return 0;
809 }
810 
811 /******************************************************************************
812  * 				MCIAVI_mciUpdate			[internal]
813  */
814 static	DWORD	MCIAVI_mciUpdate(UINT wDevID, DWORD dwFlags, LPMCI_DGV_UPDATE_PARMS lpParms)
815 {
816     WINE_MCIAVI *wma;
817 
818     TRACE("%04x, %08x, %p\n", wDevID, dwFlags, lpParms);
819 
820     if (lpParms == NULL)	return MCIERR_NULL_PARAMETER_BLOCK;
821 
822     wma = MCIAVI_mciGetOpenDev(wDevID);
823     if (wma == NULL)		return MCIERR_INVALID_DEVICE_ID;
824     /* Ignore MCI_TEST flag. */
825 
826     EnterCriticalSection(&wma->cs);
827 
828     if (dwFlags & MCI_DGV_UPDATE_HDC)
829         MCIAVI_PaintFrame(wma, lpParms->hDC);
830 
831     LeaveCriticalSection(&wma->cs);
832 
833     return 0;
834 }
835 
836 /******************************************************************************
837  * 				MCIAVI_mciStep			[internal]
838  */
839 static	DWORD	MCIAVI_mciStep(UINT wDevID, DWORD dwFlags, LPMCI_DGV_STEP_PARMS lpParms)
840 {
841     WINE_MCIAVI *wma;
842     DWORD position;
843     int delta = 1;
844 
845     TRACE("(%04x, %08x, %p)\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 
852     if (dwFlags & MCI_DGV_STEP_FRAMES)  delta = lpParms->dwFrames;
853     if (dwFlags & MCI_DGV_STEP_REVERSE) delta = -delta;
854     position = wma->dwCurrVideoFrame + delta;
855     if (position >= wma->dwPlayableVideoFrames) return MCIERR_OUTOFRANGE;
856     if (dwFlags & MCI_TEST)	return 0;
857 
858     MCIAVI_mciStop(wDevID, MCI_WAIT, NULL);
859 
860     EnterCriticalSection(&wma->cs);
861 
862     wma->dwCurrVideoFrame = position;
863     TRACE("Stepping to frame=%u\n", wma->dwCurrVideoFrame);
864 
865     if (dwFlags & MCI_NOTIFY) {
866 	mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
867                        wDevID, MCI_NOTIFY_SUCCESSFUL);
868     }
869     LeaveCriticalSection(&wma->cs);
870     return 0;
871 }
872 
873 /******************************************************************************
874  * 				MCIAVI_mciCue			[internal]
875  */
876 static	DWORD	MCIAVI_mciCue(UINT wDevID, DWORD dwFlags, LPMCI_DGV_CUE_PARMS lpParms)
877 {
878     WINE_MCIAVI *wma;
879 
880     FIXME("(%04x, %08x, %p) : stub\n", wDevID, dwFlags, lpParms);
881 
882     if (lpParms == NULL)	return MCIERR_NULL_PARAMETER_BLOCK;
883 
884     wma = MCIAVI_mciGetOpenDev(wDevID);
885     if (wma == NULL)		return MCIERR_INVALID_DEVICE_ID;
886     if (dwFlags & MCI_DGV_CUE_INPUT) return MCIERR_UNSUPPORTED_FUNCTION;
887     if (dwFlags & MCI_TEST)	return 0;
888 
889     return 0;
890 }
891 
892 /******************************************************************************
893  * 				MCIAVI_mciBreak			[internal]
894  */
895 static	DWORD	MCIAVI_mciBreak(UINT wDevID, DWORD dwFlags, LPMCI_BREAK_PARMS lpParms)
896 {
897     WINE_MCIAVI *wma;
898 
899     TRACE("(%04x, %08x, %p)\n", wDevID, dwFlags, lpParms);
900 
901     if (lpParms == NULL)	return MCIERR_NULL_PARAMETER_BLOCK;
902 
903     wma = MCIAVI_mciGetOpenDev(wDevID);
904     if (wma == NULL)		return MCIERR_INVALID_DEVICE_ID;
905 
906     EnterCriticalSection(&wma->cs);
907 
908     wma->mci_break.flags = dwFlags;
909     wma->mci_break.parms = *lpParms;
910 
911     LeaveCriticalSection(&wma->cs);
912 
913     return 0;
914 }
915 
916 /******************************************************************************
917  * 				MCIAVI_mciSetAudio			[internal]
918  */
919 static	DWORD	MCIAVI_mciSetAudio(UINT wDevID, DWORD dwFlags, LPMCI_DGV_SETAUDIO_PARMSW lpParms)
920 {
921     WINE_MCIAVI *wma;
922 
923     if (lpParms == NULL)	return MCIERR_NULL_PARAMETER_BLOCK;
924 
925     FIXME("(%04x, %08x, %p) Item %04x: stub\n", wDevID, dwFlags, lpParms, dwFlags & MCI_DGV_SETAUDIO_ITEM ? lpParms->dwItem : 0);
926 
927     wma = MCIAVI_mciGetOpenDev(wDevID);
928     if (wma == NULL)		return MCIERR_INVALID_DEVICE_ID;
929 
930     return 0;
931 }
932 
933 /******************************************************************************
934  * 				MCIAVI_mciSignal			[internal]
935  */
936 static	DWORD	MCIAVI_mciSignal(UINT wDevID, DWORD dwFlags, LPMCI_DGV_SIGNAL_PARMS lpParms)
937 {
938     WINE_MCIAVI *wma;
939 
940     FIXME("(%04x, %08x, %p) : stub\n", wDevID, dwFlags, lpParms);
941 
942     if (lpParms == NULL)	return MCIERR_NULL_PARAMETER_BLOCK;
943 
944     wma = MCIAVI_mciGetOpenDev(wDevID);
945     if (wma == NULL)		return MCIERR_INVALID_DEVICE_ID;
946 
947     return 0;
948 }
949 
950 /******************************************************************************
951  * 				MCIAVI_mciSetVideo			[internal]
952  */
953 static	DWORD	MCIAVI_mciSetVideo(UINT wDevID, DWORD dwFlags, LPMCI_DGV_SETVIDEO_PARMSW lpParms)
954 {
955     WINE_MCIAVI *wma;
956 
957     if (lpParms == NULL)	return MCIERR_NULL_PARAMETER_BLOCK;
958 
959     FIXME("(%04x, %08x, %p) Item %04x: stub\n", wDevID, dwFlags, lpParms, dwFlags & MCI_DGV_SETVIDEO_ITEM ? lpParms->dwItem : 0);
960 
961     wma = MCIAVI_mciGetOpenDev(wDevID);
962     if (wma == NULL)		return MCIERR_INVALID_DEVICE_ID;
963 
964     return 0;
965 }
966 
967 /******************************************************************************
968  * 				MCIAVI_mciConfigure			[internal]
969  */
970 static	DWORD	MCIAVI_mciConfigure(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
971 {
972     WINE_MCIAVI *wma;
973 
974     FIXME("(%04x, %08x, %p) : stub\n", wDevID, dwFlags, lpParms);
975 
976     if (lpParms == NULL)	return MCIERR_NULL_PARAMETER_BLOCK;
977 
978     wma = MCIAVI_mciGetOpenDev(wDevID);
979     if (wma == NULL)		return MCIERR_INVALID_DEVICE_ID;
980     if (dwFlags & MCI_TEST)	return 0;
981 
982     return 0;
983 }
984 
985 /*======================================================================*
986  *                  	    MCI AVI entry points			*
987  *======================================================================*/
988 
989 /**************************************************************************
990  * 				DriverProc (MCIAVI.@)
991  */
992 LRESULT CALLBACK MCIAVI_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg,
993                                    LPARAM dwParam1, LPARAM dwParam2)
994 {
995     TRACE("(%08lX, %p, %08X, %08lX, %08lX)\n",
996 	  dwDevID, hDriv, wMsg, dwParam1, dwParam2);
997 
998     switch (wMsg) {
999     case DRV_LOAD:		return 1;
1000     case DRV_FREE:		return 1;
1001     case DRV_OPEN:		return MCIAVI_drvOpen((LPCWSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSW)dwParam2);
1002     case DRV_CLOSE:		return MCIAVI_drvClose(dwDevID);
1003     case DRV_ENABLE:		return 1;
1004     case DRV_DISABLE:		return 1;
1005     case DRV_QUERYCONFIGURE:	return 1;
1006     case DRV_CONFIGURE:		return MCIAVI_drvConfigure(dwDevID);
1007     case DRV_INSTALL:		return DRVCNF_RESTART;
1008     case DRV_REMOVE:		return DRVCNF_RESTART;
1009     }
1010 
1011     /* session instance */
1012     if (dwDevID == 0xFFFFFFFF) return 1;
1013 
1014     switch (wMsg) {
1015     case MCI_OPEN_DRIVER:	return MCIAVI_mciOpen      (dwDevID, dwParam1, (LPMCI_DGV_OPEN_PARMSW)     dwParam2);
1016     case MCI_CLOSE_DRIVER:	return MCIAVI_mciClose     (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)       dwParam2);
1017     case MCI_PLAY:		return MCIAVI_mciPlay      (dwDevID, dwParam1, (LPMCI_PLAY_PARMS)          dwParam2);
1018     case MCI_STOP:		return MCIAVI_mciStop      (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)       dwParam2);
1019     case MCI_SET:		return MCIAVI_mciSet       (dwDevID, dwParam1, (LPMCI_DGV_SET_PARMS)       dwParam2);
1020     case MCI_PAUSE:		return MCIAVI_mciPause     (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)       dwParam2);
1021     case MCI_RESUME:		return MCIAVI_mciResume    (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)       dwParam2);
1022     case MCI_STATUS:		return MCIAVI_mciStatus    (dwDevID, dwParam1, (LPMCI_DGV_STATUS_PARMSW)   dwParam2);
1023     case MCI_GETDEVCAPS:	return MCIAVI_mciGetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS)    dwParam2);
1024     case MCI_INFO:		return MCIAVI_mciInfo      (dwDevID, dwParam1, (LPMCI_DGV_INFO_PARMSW)     dwParam2);
1025     case MCI_SEEK:		return MCIAVI_mciSeek      (dwDevID, dwParam1, (LPMCI_SEEK_PARMS)          dwParam2);
1026     case MCI_PUT:		return MCIAVI_mciPut	   (dwDevID, dwParam1, (LPMCI_DGV_PUT_PARMS)       dwParam2);
1027     case MCI_WINDOW:		return MCIAVI_mciWindow	   (dwDevID, dwParam1, (LPMCI_DGV_WINDOW_PARMSW)   dwParam2);
1028     case MCI_LOAD:		return MCIAVI_mciLoad      (dwDevID, dwParam1, (LPMCI_DGV_LOAD_PARMSW)     dwParam2);
1029     case MCI_REALIZE:		return MCIAVI_mciRealize   (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)       dwParam2);
1030     case MCI_UPDATE:		return MCIAVI_mciUpdate    (dwDevID, dwParam1, (LPMCI_DGV_UPDATE_PARMS)    dwParam2);
1031     case MCI_WHERE:		return MCIAVI_mciWhere	   (dwDevID, dwParam1, (LPMCI_DGV_RECT_PARMS)      dwParam2);
1032     case MCI_STEP:		return MCIAVI_mciStep      (dwDevID, dwParam1, (LPMCI_DGV_STEP_PARMS)      dwParam2);
1033     case MCI_CUE:		return MCIAVI_mciCue       (dwDevID, dwParam1, (LPMCI_DGV_CUE_PARMS)       dwParam2);
1034     case MCI_BREAK:		return MCIAVI_mciBreak     (dwDevID, dwParam1, (LPMCI_BREAK_PARMS)         dwParam2);
1035 	/* Digital Video specific */
1036     case MCI_SETAUDIO:		return MCIAVI_mciSetAudio  (dwDevID, dwParam1, (LPMCI_DGV_SETAUDIO_PARMSW) dwParam2);
1037     case MCI_SIGNAL:		return MCIAVI_mciSignal    (dwDevID, dwParam1, (LPMCI_DGV_SIGNAL_PARMS)    dwParam2);
1038     case MCI_SETVIDEO:		return MCIAVI_mciSetVideo  (dwDevID, dwParam1, (LPMCI_DGV_SETVIDEO_PARMSW) dwParam2);
1039     case MCI_CONFIGURE:		return MCIAVI_mciConfigure (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)       dwParam2);
1040 
1041 	/* no editing, recording, saving, locking without inputs */
1042     case MCI_CAPTURE:
1043     case MCI_COPY:
1044     case MCI_CUT:
1045     case MCI_DELETE:
1046     case MCI_FREEZE:
1047     case MCI_LIST:
1048     case MCI_MONITOR:
1049     case MCI_PASTE:
1050     case MCI_QUALITY:
1051     case MCI_RECORD:
1052     case MCI_RESERVE:
1053     case MCI_RESTORE:
1054     case MCI_SAVE:
1055     case MCI_UNDO:
1056     case MCI_UNFREEZE:
1057 	TRACE("Unsupported function [0x%x] flags=%08x\n", wMsg, (DWORD)dwParam1);
1058 	return MCIERR_UNSUPPORTED_FUNCTION;
1059     case MCI_SPIN:
1060     case MCI_ESCAPE:
1061 	WARN("Unsupported command [0x%x] %08x\n", wMsg, (DWORD)dwParam1);
1062 	break;
1063     case MCI_OPEN:
1064     case MCI_CLOSE:
1065 	FIXME("Shouldn't receive a MCI_OPEN or CLOSE message\n");
1066 	break;
1067     default:
1068 	TRACE("Sending msg [%u] to default driver proc\n", wMsg);
1069 	return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1070     }
1071     return MCIERR_UNRECOGNIZED_COMMAND;
1072 }
1073