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