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