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