xref: /reactos/dll/win32/mciqtz32/mciqtz.c (revision b8dd046e)
1 /*
2  * DirectShow MCI Driver
3  *
4  * Copyright 2009 Christian Costa
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20 
21 #include <stdarg.h>
22 #include <math.h>
23 #include "windef.h"
24 #include "winbase.h"
25 #include "winuser.h"
26 #include "mmddk.h"
27 #include "wine/debug.h"
28 #include "mciqtz_private.h"
29 #include "digitalv.h"
30 #include "wownt32.h"
31 
32 WINE_DEFAULT_DEBUG_CHANNEL(mciqtz);
33 
34 static DWORD MCIQTZ_mciClose(UINT, DWORD, LPMCI_GENERIC_PARMS);
35 static DWORD MCIQTZ_mciStop(UINT, DWORD, LPMCI_GENERIC_PARMS);
36 
37 /*======================================================================*
38  *                          MCI QTZ implementation                      *
39  *======================================================================*/
40 
41 static HINSTANCE MCIQTZ_hInstance = 0;
42 
43 /***********************************************************************
44  *              DllMain (MCIQTZ.0)
45  */
46 BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD fdwReason, LPVOID fImpLoad)
47 {
48     switch (fdwReason) {
49     case DLL_PROCESS_ATTACH:
50         DisableThreadLibraryCalls(hInstDLL);
51         MCIQTZ_hInstance = hInstDLL;
52         break;
53     }
54     return TRUE;
55 }
56 
57 /**************************************************************************
58  *                              MCIQTZ_mciGetOpenDev            [internal]
59  */
60 static WINE_MCIQTZ* MCIQTZ_mciGetOpenDev(UINT wDevID)
61 {
62     WINE_MCIQTZ* wma = (WINE_MCIQTZ*)mciGetDriverData(wDevID);
63 
64     if (!wma) {
65         WARN("Invalid wDevID=%u\n", wDevID);
66         return NULL;
67     }
68     return wma;
69 }
70 
71 /**************************************************************************
72  *                              MCIQTZ_drvOpen                  [internal]
73  */
74 static DWORD MCIQTZ_drvOpen(LPCWSTR str, LPMCI_OPEN_DRIVER_PARMSW modp)
75 {
76     WINE_MCIQTZ* wma;
77     static const WCHAR mciAviWStr[] = {'M','C','I','A','V','I',0};
78 
79     TRACE("(%s, %p)\n", debugstr_w(str), modp);
80 
81     /* session instance */
82     if (!modp)
83         return 0xFFFFFFFF;
84 
85     wma = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINE_MCIQTZ));
86     if (!wma)
87         return 0;
88 
89     wma->stop_event = CreateEventW(NULL, FALSE, FALSE, NULL);
90     modp->wType = MCI_DEVTYPE_DIGITAL_VIDEO;
91     wma->wDevID = modp->wDeviceID;
92     modp->wCustomCommandTable = wma->command_table = mciLoadCommandResource(MCIQTZ_hInstance, mciAviWStr, 0);
93     mciSetDriverData(wma->wDevID, (DWORD_PTR)wma);
94 
95     return modp->wDeviceID;
96 }
97 
98 /**************************************************************************
99  *                              MCIQTZ_drvClose         [internal]
100  */
101 static DWORD MCIQTZ_drvClose(DWORD dwDevID)
102 {
103     WINE_MCIQTZ* wma;
104 
105     TRACE("(%04x)\n", dwDevID);
106 
107     wma = MCIQTZ_mciGetOpenDev(dwDevID);
108 
109     if (wma) {
110         /* finish all outstanding things */
111         MCIQTZ_mciClose(dwDevID, MCI_WAIT, NULL);
112 
113         mciFreeCommandResource(wma->command_table);
114         mciSetDriverData(dwDevID, 0);
115         CloseHandle(wma->stop_event);
116         HeapFree(GetProcessHeap(), 0, wma);
117         return 1;
118     }
119 
120     return (dwDevID == 0xFFFFFFFF) ? 1 : 0;
121 }
122 
123 /**************************************************************************
124  *                              MCIQTZ_drvConfigure             [internal]
125  */
126 static DWORD MCIQTZ_drvConfigure(DWORD dwDevID)
127 {
128     WINE_MCIQTZ* wma;
129 
130     TRACE("(%04x)\n", dwDevID);
131 
132     wma = MCIQTZ_mciGetOpenDev(dwDevID);
133     if (!wma)
134         return 0;
135 
136     MCIQTZ_mciStop(dwDevID, MCI_WAIT, NULL);
137 
138     MessageBoxA(0, "Sample QTZ Wine Driver !", "MM-Wine Driver", MB_OK);
139 
140     return 1;
141 }
142 
143 /**************************************************************************
144  *                              MCIQTZ_mciNotify                [internal]
145  *
146  * Notifications in MCI work like a 1-element queue.
147  * Each new notification request supersedes the previous one.
148  */
149 static void MCIQTZ_mciNotify(DWORD_PTR hWndCallBack, WINE_MCIQTZ* wma, UINT wStatus)
150 {
151     MCIDEVICEID wDevID = wma->notify_devid;
152     HANDLE old = InterlockedExchangePointer(&wma->callback, NULL);
153     if (old) mciDriverNotify(old, wDevID, MCI_NOTIFY_SUPERSEDED);
154     mciDriverNotify(HWND_32(LOWORD(hWndCallBack)), wDevID, wStatus);
155 }
156 
157 /***************************************************************************
158  *                              MCIQTZ_mciOpen                  [internal]
159  */
160 static DWORD MCIQTZ_mciOpen(UINT wDevID, DWORD dwFlags,
161                             LPMCI_DGV_OPEN_PARMSW lpOpenParms)
162 {
163     WINE_MCIQTZ* wma;
164     HRESULT hr;
165     DWORD style = 0;
166     RECT rc = { 0, 0, 0, 0 };
167 
168     TRACE("(%04x, %08X, %p)\n", wDevID, dwFlags, lpOpenParms);
169 
170     if(!lpOpenParms)
171         return MCIERR_NULL_PARAMETER_BLOCK;
172 
173     wma = MCIQTZ_mciGetOpenDev(wDevID);
174     if (!wma)
175         return MCIERR_INVALID_DEVICE_ID;
176 
177     MCIQTZ_mciStop(wDevID, MCI_WAIT, NULL);
178 
179     hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
180     wma->uninit = SUCCEEDED(hr);
181 
182     hr = CoCreateInstance(&CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, &IID_IGraphBuilder, (LPVOID*)&wma->pgraph);
183     if (FAILED(hr)) {
184         TRACE("Cannot create filtergraph (hr = %x)\n", hr);
185         goto err;
186     }
187 
188     hr = IGraphBuilder_QueryInterface(wma->pgraph, &IID_IMediaControl, (LPVOID*)&wma->pmctrl);
189     if (FAILED(hr)) {
190         TRACE("Cannot get IMediaControl interface (hr = %x)\n", hr);
191         goto err;
192     }
193 
194     hr = IGraphBuilder_QueryInterface(wma->pgraph, &IID_IMediaSeeking, (void**)&wma->seek);
195     if (FAILED(hr)) {
196         TRACE("Cannot get IMediaSeeking interface (hr = %x)\n", hr);
197         goto err;
198     }
199 
200     hr = IGraphBuilder_QueryInterface(wma->pgraph, &IID_IMediaEvent, (void**)&wma->mevent);
201     if (FAILED(hr)) {
202         TRACE("Cannot get IMediaEvent interface (hr = %x)\n", hr);
203         goto err;
204     }
205 
206     hr = IGraphBuilder_QueryInterface(wma->pgraph, &IID_IVideoWindow, (void**)&wma->vidwin);
207     if (FAILED(hr)) {
208         TRACE("Cannot get IVideoWindow interface (hr = %x)\n", hr);
209         goto err;
210     }
211 
212     hr = IGraphBuilder_QueryInterface(wma->pgraph, &IID_IBasicVideo, (void**)&wma->vidbasic);
213     if (FAILED(hr)) {
214         TRACE("Cannot get IBasicVideo interface (hr = %x)\n", hr);
215         goto err;
216     }
217 
218     hr = IGraphBuilder_QueryInterface(wma->pgraph, &IID_IBasicAudio, (void**)&wma->audio);
219     if (FAILED(hr)) {
220         TRACE("Cannot get IBasicAudio interface (hr = %x)\n", hr);
221         goto err;
222     }
223 
224     if (!(dwFlags & MCI_OPEN_ELEMENT) || (dwFlags & MCI_OPEN_ELEMENT_ID)) {
225         TRACE("Wrong dwFlags %x\n", dwFlags);
226         goto err;
227     }
228 
229     if (!lpOpenParms->lpstrElementName || !lpOpenParms->lpstrElementName[0]) {
230         TRACE("Invalid filename specified\n");
231         goto err;
232     }
233 
234     TRACE("Open file %s\n", debugstr_w(lpOpenParms->lpstrElementName));
235 
236     hr = IGraphBuilder_RenderFile(wma->pgraph, lpOpenParms->lpstrElementName, NULL);
237     if (FAILED(hr)) {
238         TRACE("Cannot render file (hr = %x)\n", hr);
239         goto err;
240     }
241 
242     IVideoWindow_put_AutoShow(wma->vidwin, OAFALSE);
243     IVideoWindow_put_Visible(wma->vidwin, OAFALSE);
244     if (dwFlags & MCI_DGV_OPEN_WS)
245         style = lpOpenParms->dwStyle;
246     if (dwFlags & MCI_DGV_OPEN_PARENT) {
247         IVideoWindow_put_MessageDrain(wma->vidwin, (OAHWND)lpOpenParms->hWndParent);
248         IVideoWindow_put_WindowState(wma->vidwin, SW_HIDE);
249         IVideoWindow_put_WindowStyle(wma->vidwin, style|WS_CHILD);
250         IVideoWindow_put_Owner(wma->vidwin, (OAHWND)lpOpenParms->hWndParent);
251         GetClientRect(lpOpenParms->hWndParent, &rc);
252         IVideoWindow_SetWindowPosition(wma->vidwin, rc.left, rc.top, rc.right - rc.top, rc.bottom - rc.top);
253         wma->parent = (HWND)lpOpenParms->hWndParent;
254     }
255     else if (style)
256         IVideoWindow_put_WindowStyle(wma->vidwin, style);
257     IBasicVideo_GetVideoSize(wma->vidbasic, &rc.right, &rc.bottom);
258     wma->opened = TRUE;
259 
260     if (dwFlags & MCI_NOTIFY)
261         mciDriverNotify(HWND_32(LOWORD(lpOpenParms->dwCallback)), wDevID, MCI_NOTIFY_SUCCESSFUL);
262 
263     return 0;
264 
265 err:
266     if (wma->audio)
267         IBasicAudio_Release(wma->audio);
268     wma->audio = NULL;
269     if (wma->vidbasic)
270         IBasicVideo_Release(wma->vidbasic);
271     wma->vidbasic = NULL;
272     if (wma->seek)
273         IMediaSeeking_Release(wma->seek);
274     wma->seek = NULL;
275     if (wma->vidwin)
276         IVideoWindow_Release(wma->vidwin);
277     wma->vidwin = NULL;
278     if (wma->pgraph)
279         IGraphBuilder_Release(wma->pgraph);
280     wma->pgraph = NULL;
281     if (wma->mevent)
282         IMediaEvent_Release(wma->mevent);
283     wma->mevent = NULL;
284     if (wma->pmctrl)
285         IMediaControl_Release(wma->pmctrl);
286     wma->pmctrl = NULL;
287 
288     if (wma->uninit)
289         CoUninitialize();
290     wma->uninit = FALSE;
291 
292     return MCIERR_INTERNAL;
293 }
294 
295 /***************************************************************************
296  *                              MCIQTZ_mciClose                 [internal]
297  */
298 static DWORD MCIQTZ_mciClose(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
299 {
300     WINE_MCIQTZ* wma;
301 
302     TRACE("(%04x, %08X, %p)\n", wDevID, dwFlags, lpParms);
303 
304     wma = MCIQTZ_mciGetOpenDev(wDevID);
305     if (!wma)
306         return MCIERR_INVALID_DEVICE_ID;
307 
308     MCIQTZ_mciStop(wDevID, MCI_WAIT, NULL);
309 
310     if (wma->opened) {
311         IVideoWindow_Release(wma->vidwin);
312         IBasicVideo_Release(wma->vidbasic);
313         IBasicAudio_Release(wma->audio);
314         IMediaSeeking_Release(wma->seek);
315         IMediaEvent_Release(wma->mevent);
316         IGraphBuilder_Release(wma->pgraph);
317         IMediaControl_Release(wma->pmctrl);
318         if (wma->uninit)
319             CoUninitialize();
320         wma->opened = FALSE;
321     }
322 
323     return 0;
324 }
325 
326 /***************************************************************************
327  *                              MCIQTZ_notifyThread             [internal]
328  */
329 static DWORD CALLBACK MCIQTZ_notifyThread(LPVOID parm)
330 {
331     WINE_MCIQTZ* wma = (WINE_MCIQTZ *)parm;
332     HRESULT hr;
333     HANDLE handle[2];
334     DWORD n = 0, ret = 0;
335 
336     handle[n++] = wma->stop_event;
337     IMediaEvent_GetEventHandle(wma->mevent, (OAEVENT *)&handle[n++]);
338 
339     for (;;) {
340         DWORD r;
341         HANDLE old;
342 
343         r = WaitForMultipleObjects(n, handle, FALSE, INFINITE);
344         if (r == WAIT_OBJECT_0) {
345             TRACE("got stop event\n");
346             old = InterlockedExchangePointer(&wma->callback, NULL);
347             if (old)
348                 mciDriverNotify(old, wma->notify_devid, MCI_NOTIFY_ABORTED);
349             break;
350         }
351         else if (r == WAIT_OBJECT_0+1) {
352             LONG event_code;
353             LONG_PTR p1, p2;
354             do {
355                 hr = IMediaEvent_GetEvent(wma->mevent, &event_code, &p1, &p2, 0);
356                 if (SUCCEEDED(hr)) {
357                     TRACE("got event_code = 0x%02x\n", event_code);
358                     IMediaEvent_FreeEventParams(wma->mevent, event_code, p1, p2);
359                 }
360             } while (hr == S_OK && event_code != EC_COMPLETE);
361             if (hr == S_OK && event_code == EC_COMPLETE) {
362                 /* Repeat the music by seeking and running again */
363                 if (wma->mci_flags & MCI_DGV_PLAY_REPEAT) {
364                     TRACE("repeat media as requested\n");
365                     IMediaControl_Stop(wma->pmctrl);
366                     IMediaSeeking_SetPositions(wma->seek,
367                                                &wma->seek_start,
368                                                AM_SEEKING_AbsolutePositioning,
369                                                &wma->seek_stop,
370                                                AM_SEEKING_AbsolutePositioning);
371                     IMediaControl_Run(wma->pmctrl);
372                     continue;
373                 }
374                 old = InterlockedExchangePointer(&wma->callback, NULL);
375                 if (old)
376                     mciDriverNotify(old, wma->notify_devid, MCI_NOTIFY_SUCCESSFUL);
377                 break;
378             }
379         }
380         else {
381             TRACE("Unknown error (%d)\n", (int)r);
382             break;
383         }
384     }
385 
386     hr = IMediaControl_Stop(wma->pmctrl);
387     if (FAILED(hr)) {
388         TRACE("Cannot stop filtergraph (hr = %x)\n", hr);
389         ret = MCIERR_INTERNAL;
390     }
391 
392     return ret;
393 }
394 
395 /***************************************************************************
396  *                              MCIQTZ_mciPlay                  [internal]
397  */
398 static DWORD MCIQTZ_mciPlay(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
399 {
400     WINE_MCIQTZ* wma;
401     HRESULT hr;
402     GUID format;
403     DWORD start_flags;
404 
405     TRACE("(%04x, %08X, %p)\n", wDevID, dwFlags, lpParms);
406 
407     if(!lpParms)
408         return MCIERR_NULL_PARAMETER_BLOCK;
409 
410     wma = MCIQTZ_mciGetOpenDev(wDevID);
411     if (!wma)
412         return MCIERR_INVALID_DEVICE_ID;
413 
414     ResetEvent(wma->stop_event);
415     if (dwFlags & MCI_NOTIFY) {
416         HANDLE old;
417         old = InterlockedExchangePointer(&wma->callback, HWND_32(LOWORD(lpParms->dwCallback)));
418         if (old)
419             mciDriverNotify(old, wma->notify_devid, MCI_NOTIFY_ABORTED);
420     }
421 
422     wma->mci_flags = dwFlags;
423     IMediaSeeking_GetTimeFormat(wma->seek, &format);
424     if (dwFlags & MCI_FROM) {
425         wma->seek_start = lpParms->dwFrom;
426         if (IsEqualGUID(&format, &TIME_FORMAT_MEDIA_TIME))
427             wma->seek_start *= 10000;
428 
429         start_flags = AM_SEEKING_AbsolutePositioning;
430     } else {
431         wma->seek_start = 0;
432         start_flags = AM_SEEKING_NoPositioning;
433     }
434     if (dwFlags & MCI_TO) {
435         wma->seek_stop = lpParms->dwTo;
436         if (IsEqualGUID(&format, &TIME_FORMAT_MEDIA_TIME))
437             wma->seek_stop *= 10000;
438     } else {
439         wma->seek_stop = 0;
440         IMediaSeeking_GetDuration(wma->seek, &wma->seek_stop);
441     }
442     IMediaSeeking_SetPositions(wma->seek, &wma->seek_start, start_flags,
443                                &wma->seek_stop, AM_SEEKING_AbsolutePositioning);
444 
445     hr = IMediaControl_Run(wma->pmctrl);
446     if (FAILED(hr)) {
447         TRACE("Cannot run filtergraph (hr = %x)\n", hr);
448         return MCIERR_INTERNAL;
449     }
450 
451     IVideoWindow_put_Visible(wma->vidwin, OATRUE);
452 
453     wma->thread = CreateThread(NULL, 0, MCIQTZ_notifyThread, wma, 0, NULL);
454     if (!wma->thread) {
455         TRACE("Can't create thread\n");
456         return MCIERR_INTERNAL;
457     }
458     return 0;
459 }
460 
461 /***************************************************************************
462  *                              MCIQTZ_mciSeek                  [internal]
463  */
464 static DWORD MCIQTZ_mciSeek(UINT wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
465 {
466     WINE_MCIQTZ* wma;
467     HRESULT hr;
468     LONGLONG newpos;
469 
470     TRACE("(%04x, %08X, %p)\n", wDevID, dwFlags, lpParms);
471 
472     if(!lpParms)
473         return MCIERR_NULL_PARAMETER_BLOCK;
474 
475     wma = MCIQTZ_mciGetOpenDev(wDevID);
476     if (!wma)
477         return MCIERR_INVALID_DEVICE_ID;
478 
479     MCIQTZ_mciStop(wDevID, MCI_WAIT, NULL);
480 
481     if (dwFlags & MCI_SEEK_TO_START) {
482         newpos = 0;
483     } else if (dwFlags & MCI_SEEK_TO_END) {
484         FIXME("MCI_SEEK_TO_END not implemented yet\n");
485         return MCIERR_INTERNAL;
486     } else if (dwFlags & MCI_TO) {
487         FIXME("MCI_TO not implemented yet\n");
488         return MCIERR_INTERNAL;
489     } else {
490         WARN("dwFlag doesn't tell where to seek to...\n");
491         return MCIERR_MISSING_PARAMETER;
492     }
493 
494     hr = IMediaSeeking_SetPositions(wma->seek, &newpos, AM_SEEKING_AbsolutePositioning, NULL, AM_SEEKING_NoPositioning);
495     if (FAILED(hr)) {
496         FIXME("Cannot set position (hr = %x)\n", hr);
497         return MCIERR_INTERNAL;
498     }
499 
500     if (dwFlags & MCI_NOTIFY)
501         MCIQTZ_mciNotify(lpParms->dwCallback, wma, MCI_NOTIFY_SUCCESSFUL);
502 
503     return 0;
504 }
505 
506 /***************************************************************************
507  *                              MCIQTZ_mciStop                  [internal]
508  */
509 static DWORD MCIQTZ_mciStop(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
510 {
511     WINE_MCIQTZ* wma;
512 
513     TRACE("(%04x, %08X, %p)\n", wDevID, dwFlags, lpParms);
514 
515     wma = MCIQTZ_mciGetOpenDev(wDevID);
516     if (!wma)
517         return MCIERR_INVALID_DEVICE_ID;
518 
519     if (!wma->opened)
520         return 0;
521 
522     if (wma->thread) {
523         SetEvent(wma->stop_event);
524         WaitForSingleObject(wma->thread, INFINITE);
525         CloseHandle(wma->thread);
526         wma->thread = NULL;
527     }
528 
529     if (!wma->parent)
530         IVideoWindow_put_Visible(wma->vidwin, OAFALSE);
531 
532     return 0;
533 }
534 
535 /***************************************************************************
536  *                              MCIQTZ_mciPause                 [internal]
537  */
538 static DWORD MCIQTZ_mciPause(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
539 {
540     WINE_MCIQTZ* wma;
541     HRESULT hr;
542 
543     TRACE("(%04x, %08X, %p)\n", wDevID, dwFlags, lpParms);
544 
545     wma = MCIQTZ_mciGetOpenDev(wDevID);
546     if (!wma)
547         return MCIERR_INVALID_DEVICE_ID;
548 
549     hr = IMediaControl_Pause(wma->pmctrl);
550     if (FAILED(hr)) {
551         TRACE("Cannot pause filtergraph (hr = %x)\n", hr);
552         return MCIERR_INTERNAL;
553     }
554 
555     return 0;
556 }
557 
558 /***************************************************************************
559  *                              MCIQTZ_mciResume                 [internal]
560  */
561 static DWORD MCIQTZ_mciResume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
562 {
563     WINE_MCIQTZ* wma;
564     HRESULT hr;
565 
566     TRACE("(%04x, %08X, %p)\n", wDevID, dwFlags, lpParms);
567 
568     wma = MCIQTZ_mciGetOpenDev(wDevID);
569     if (!wma)
570         return MCIERR_INVALID_DEVICE_ID;
571 
572     hr = IMediaControl_Run(wma->pmctrl);
573     if (FAILED(hr)) {
574         TRACE("Cannot run filtergraph (hr = %x)\n", hr);
575         return MCIERR_INTERNAL;
576     }
577 
578     return 0;
579 }
580 
581 /***************************************************************************
582  *                              MCIQTZ_mciGetDevCaps            [internal]
583  */
584 static DWORD MCIQTZ_mciGetDevCaps(UINT wDevID, DWORD dwFlags, LPMCI_GETDEVCAPS_PARMS lpParms)
585 {
586     WINE_MCIQTZ* wma;
587 
588     TRACE("(%04x, %08X, %p)\n", wDevID, dwFlags, lpParms);
589 
590     if(!lpParms)
591         return MCIERR_NULL_PARAMETER_BLOCK;
592 
593     wma = MCIQTZ_mciGetOpenDev(wDevID);
594     if (!wma)
595         return MCIERR_INVALID_DEVICE_ID;
596 
597     if (!(dwFlags & MCI_GETDEVCAPS_ITEM))
598         return MCIERR_MISSING_PARAMETER;
599 
600     switch (lpParms->dwItem) {
601         case MCI_GETDEVCAPS_CAN_RECORD:
602             lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
603             TRACE("MCI_GETDEVCAPS_CAN_RECORD = %08x\n", lpParms->dwReturn);
604             break;
605         case MCI_GETDEVCAPS_HAS_AUDIO:
606             lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
607             TRACE("MCI_GETDEVCAPS_HAS_AUDIO = %08x\n", lpParms->dwReturn);
608             break;
609         case MCI_GETDEVCAPS_HAS_VIDEO:
610             lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
611             TRACE("MCI_GETDEVCAPS_HAS_VIDEO = %08x\n", lpParms->dwReturn);
612             break;
613         case MCI_GETDEVCAPS_DEVICE_TYPE:
614             lpParms->dwReturn = MAKEMCIRESOURCE(MCI_DEVTYPE_DIGITAL_VIDEO, MCI_DEVTYPE_DIGITAL_VIDEO);
615             TRACE("MCI_GETDEVCAPS_DEVICE_TYPE = %08x\n", lpParms->dwReturn);
616             break;
617         case MCI_GETDEVCAPS_USES_FILES:
618             lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
619             TRACE("MCI_GETDEVCAPS_USES_FILES = %08x\n", lpParms->dwReturn);
620             break;
621         case MCI_GETDEVCAPS_COMPOUND_DEVICE:
622             lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
623             TRACE("MCI_GETDEVCAPS_COMPOUND_DEVICE = %08x\n", lpParms->dwReturn);
624             break;
625         case MCI_GETDEVCAPS_CAN_EJECT:
626             lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
627             TRACE("MCI_GETDEVCAPS_EJECT = %08x\n", lpParms->dwReturn);
628             break;
629         case MCI_GETDEVCAPS_CAN_PLAY:
630             lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
631             TRACE("MCI_GETDEVCAPS_CAN_PLAY = %08x\n", lpParms->dwReturn);
632             break;
633         case MCI_GETDEVCAPS_CAN_SAVE:
634             lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
635             TRACE("MCI_GETDEVCAPS_CAN_SAVE = %08x\n", lpParms->dwReturn);
636             break;
637         case MCI_DGV_GETDEVCAPS_CAN_REVERSE:
638             lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
639             TRACE("MCI_DGV_GETDEVCAPS_CAN_REVERSE = %08x\n", lpParms->dwReturn);
640             break;
641         case MCI_DGV_GETDEVCAPS_CAN_STRETCH:
642             lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE); /* FIXME */
643             TRACE("MCI_DGV_GETDEVCAPS_CAN_STRETCH = %08x\n", lpParms->dwReturn);
644             break;
645         case MCI_DGV_GETDEVCAPS_CAN_LOCK:
646             lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
647             TRACE("MCI_DGV_GETDEVCAPS_CAN_LOCK = %08x\n", lpParms->dwReturn);
648             break;
649         case MCI_DGV_GETDEVCAPS_CAN_FREEZE:
650             lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
651             TRACE("MCI_DGV_GETDEVCAPS_CAN_FREEZE = %08x\n", lpParms->dwReturn);
652             break;
653         case MCI_DGV_GETDEVCAPS_CAN_STR_IN:
654             lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
655             TRACE("MCI_DGV_GETDEVCAPS_CAN_STRETCH_INPUT = %08x\n", lpParms->dwReturn);
656             break;
657         case MCI_DGV_GETDEVCAPS_HAS_STILL:
658             lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
659             TRACE("MCI_DGV_GETDEVCAPS_HAS_STILL = %08x\n", lpParms->dwReturn);
660             break;
661         case MCI_DGV_GETDEVCAPS_CAN_TEST:
662             lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE); /* FIXME */
663             TRACE("MCI_DGV_GETDEVCAPS_CAN_TEST = %08x\n", lpParms->dwReturn);
664             break;
665         case MCI_DGV_GETDEVCAPS_MAX_WINDOWS:
666             lpParms->dwReturn = 1;
667             TRACE("MCI_DGV_GETDEVCAPS_MAX_WINDOWS = %u\n", lpParms->dwReturn);
668             return 0;
669         default:
670             WARN("Unknown capability %08x\n", lpParms->dwItem);
671             /* Fall through */
672         case MCI_DGV_GETDEVCAPS_MAXIMUM_RATE: /* unknown to w2k */
673         case MCI_DGV_GETDEVCAPS_MINIMUM_RATE: /* unknown to w2k */
674             return MCIERR_UNSUPPORTED_FUNCTION;
675     }
676 
677     return MCI_RESOURCE_RETURNED;
678 }
679 
680 /***************************************************************************
681  *                              MCIQTZ_mciSet                   [internal]
682  */
683 static DWORD MCIQTZ_mciSet(UINT wDevID, DWORD dwFlags, LPMCI_DGV_SET_PARMS lpParms)
684 {
685     WINE_MCIQTZ* wma;
686 
687     TRACE("(%04x, %08X, %p)\n", wDevID, dwFlags, lpParms);
688 
689     if(!lpParms)
690         return MCIERR_NULL_PARAMETER_BLOCK;
691 
692     wma = MCIQTZ_mciGetOpenDev(wDevID);
693     if (!wma)
694         return MCIERR_INVALID_DEVICE_ID;
695 
696     if (dwFlags & MCI_SET_TIME_FORMAT) {
697         switch (lpParms->dwTimeFormat) {
698             case MCI_FORMAT_MILLISECONDS:
699                 TRACE("MCI_SET_TIME_FORMAT = MCI_FORMAT_MILLISECONDS\n");
700                 wma->time_format = MCI_FORMAT_MILLISECONDS;
701                 break;
702             case MCI_FORMAT_FRAMES:
703                 TRACE("MCI_SET_TIME_FORMAT = MCI_FORMAT_FRAMES\n");
704                 wma->time_format = MCI_FORMAT_FRAMES;
705                 break;
706             default:
707                 WARN("Bad time format %u\n", lpParms->dwTimeFormat);
708                 return MCIERR_BAD_TIME_FORMAT;
709         }
710     }
711 
712     if (dwFlags & MCI_SET_DOOR_OPEN)
713         FIXME("MCI_SET_DOOR_OPEN not implemented yet\n");
714     if (dwFlags & MCI_SET_DOOR_CLOSED)
715         FIXME("MCI_SET_DOOR_CLOSED not implemented yet\n");
716     if (dwFlags & MCI_SET_AUDIO)
717         FIXME("MCI_SET_AUDIO not implemented yet\n");
718     if (dwFlags & MCI_SET_VIDEO)
719         FIXME("MCI_SET_VIDEO not implemented yet\n");
720     if (dwFlags & MCI_SET_ON)
721         FIXME("MCI_SET_ON not implemented yet\n");
722     if (dwFlags & MCI_SET_OFF)
723         FIXME("MCI_SET_OFF not implemented yet\n");
724     if (dwFlags & MCI_SET_AUDIO_LEFT)
725         FIXME("MCI_SET_AUDIO_LEFT not implemented yet\n");
726     if (dwFlags & MCI_SET_AUDIO_RIGHT)
727         FIXME("MCI_SET_AUDIO_RIGHT not implemented yet\n");
728 
729     if (dwFlags & ~0x7f03 /* All MCI_SET flags mask */)
730         ERR("Unknown flags %08x\n", dwFlags & ~0x7f03);
731 
732     return 0;
733 }
734 
735 /***************************************************************************
736  *                              MCIQTZ_mciStatus                [internal]
737  */
738 static DWORD MCIQTZ_mciStatus(UINT wDevID, DWORD dwFlags, LPMCI_DGV_STATUS_PARMSW lpParms)
739 {
740     WINE_MCIQTZ* wma;
741     HRESULT hr;
742     DWORD ret = MCI_INTEGER_RETURNED;
743 
744     TRACE("(%04x, %08X, %p)\n", wDevID, dwFlags, lpParms);
745 
746     if(!lpParms)
747         return MCIERR_NULL_PARAMETER_BLOCK;
748 
749     wma = MCIQTZ_mciGetOpenDev(wDevID);
750     if (!wma)
751         return MCIERR_INVALID_DEVICE_ID;
752 
753     if (!(dwFlags & MCI_STATUS_ITEM)) {
754         WARN("No status item specified\n");
755         return MCIERR_UNRECOGNIZED_COMMAND;
756     }
757 
758     switch (lpParms->dwItem) {
759         case MCI_STATUS_LENGTH: {
760             LONGLONG duration = -1;
761             GUID format;
762             switch (wma->time_format) {
763                 case MCI_FORMAT_MILLISECONDS: format = TIME_FORMAT_MEDIA_TIME; break;
764                 case MCI_FORMAT_FRAMES: format = TIME_FORMAT_FRAME; break;
765                 default: ERR("Unhandled format %x\n", wma->time_format); break;
766             }
767             hr = IMediaSeeking_SetTimeFormat(wma->seek, &format);
768             if (FAILED(hr)) {
769                 FIXME("Cannot set time format (hr = %x)\n", hr);
770                 lpParms->dwReturn = 0;
771                 break;
772             }
773             hr = IMediaSeeking_GetDuration(wma->seek, &duration);
774             if (FAILED(hr) || duration < 0) {
775                 FIXME("Cannot read duration (hr = %x)\n", hr);
776                 lpParms->dwReturn = 0;
777             } else if (wma->time_format != MCI_FORMAT_MILLISECONDS)
778                 lpParms->dwReturn = duration;
779             else
780                 lpParms->dwReturn = duration / 10000;
781             break;
782         }
783         case MCI_STATUS_POSITION: {
784             REFERENCE_TIME curpos;
785 
786             hr = IMediaSeeking_GetCurrentPosition(wma->seek, &curpos);
787             if (FAILED(hr)) {
788                 FIXME("Cannot get position (hr = %x)\n", hr);
789                 return MCIERR_INTERNAL;
790             }
791             lpParms->dwReturn = curpos / 10000;
792             break;
793         }
794         case MCI_STATUS_NUMBER_OF_TRACKS:
795             FIXME("MCI_STATUS_NUMBER_OF_TRACKS not implemented yet\n");
796             return MCIERR_UNRECOGNIZED_COMMAND;
797         case MCI_STATUS_MODE: {
798             LONG state = State_Stopped;
799             IMediaControl_GetState(wma->pmctrl, -1, &state);
800             if (state == State_Stopped)
801                 lpParms->dwReturn = MAKEMCIRESOURCE(MCI_MODE_STOP, MCI_MODE_STOP);
802             else if (state == State_Running) {
803                 lpParms->dwReturn = MAKEMCIRESOURCE(MCI_MODE_PLAY, MCI_MODE_PLAY);
804                 if (!wma->thread || WaitForSingleObject(wma->thread, 0) == WAIT_OBJECT_0)
805                     lpParms->dwReturn = MAKEMCIRESOURCE(MCI_MODE_STOP, MCI_MODE_STOP);
806             } else if (state == State_Paused)
807                 lpParms->dwReturn = MAKEMCIRESOURCE(MCI_MODE_PAUSE, MCI_MODE_PAUSE);
808             ret = MCI_RESOURCE_RETURNED;
809             break;
810         }
811         case MCI_STATUS_MEDIA_PRESENT:
812             FIXME("MCI_STATUS_MEDIA_PRESENT not implemented yet\n");
813             return MCIERR_UNRECOGNIZED_COMMAND;
814         case MCI_STATUS_TIME_FORMAT:
815             lpParms->dwReturn = MAKEMCIRESOURCE(wma->time_format,
816                                                 MCI_FORMAT_RETURN_BASE + wma->time_format);
817             ret = MCI_RESOURCE_RETURNED;
818             break;
819         case MCI_STATUS_READY:
820             FIXME("MCI_STATUS_READY not implemented yet\n");
821             return MCIERR_UNRECOGNIZED_COMMAND;
822         case MCI_STATUS_CURRENT_TRACK:
823             FIXME("MCI_STATUS_CURRENT_TRACK not implemented yet\n");
824             return MCIERR_UNRECOGNIZED_COMMAND;
825         default:
826             FIXME("Unknown command %08X\n", lpParms->dwItem);
827             return MCIERR_UNRECOGNIZED_COMMAND;
828     }
829 
830     if (dwFlags & MCI_NOTIFY)
831         MCIQTZ_mciNotify(lpParms->dwCallback, wma, MCI_NOTIFY_SUCCESSFUL);
832 
833     return ret;
834 }
835 
836 /***************************************************************************
837  *                              MCIQTZ_mciWhere                 [internal]
838  */
839 static DWORD MCIQTZ_mciWhere(UINT wDevID, DWORD dwFlags, LPMCI_DGV_RECT_PARMS lpParms)
840 {
841     WINE_MCIQTZ* wma;
842     HRESULT hr;
843     HWND hWnd;
844     RECT rc;
845     DWORD ret = MCIERR_UNRECOGNIZED_COMMAND;
846 
847     TRACE("(%04x, %08X, %p)\n", wDevID, dwFlags, lpParms);
848 
849     if(!lpParms)
850         return MCIERR_NULL_PARAMETER_BLOCK;
851 
852     wma = MCIQTZ_mciGetOpenDev(wDevID);
853     if (!wma)
854         return MCIERR_INVALID_DEVICE_ID;
855 
856     hr = IVideoWindow_get_Owner(wma->vidwin, (OAHWND*)&hWnd);
857     if (FAILED(hr)) {
858         TRACE("No video stream, returning no window error\n");
859         return MCIERR_NO_WINDOW;
860     }
861 
862     if (dwFlags & MCI_DGV_WHERE_SOURCE) {
863         if (dwFlags & MCI_DGV_WHERE_MAX)
864             FIXME("MCI_DGV_WHERE_SOURCE_MAX stub\n");
865         IBasicVideo_GetSourcePosition(wma->vidbasic, &rc.left, &rc.top, &rc.right, &rc.bottom);
866         TRACE("MCI_DGV_WHERE_SOURCE %s\n", wine_dbgstr_rect(&rc));
867     }
868     if (dwFlags & MCI_DGV_WHERE_DESTINATION) {
869         if (dwFlags & MCI_DGV_WHERE_MAX)
870             FIXME("MCI_DGV_WHERE_DESTINATION_MAX stub\n");
871         IBasicVideo_GetDestinationPosition(wma->vidbasic, &rc.left, &rc.top, &rc.right, &rc.bottom);
872         TRACE("MCI_DGV_WHERE_DESTINATION %s\n", wine_dbgstr_rect(&rc));
873     }
874     if (dwFlags & MCI_DGV_WHERE_FRAME) {
875         if (dwFlags & MCI_DGV_WHERE_MAX)
876             FIXME("MCI_DGV_WHERE_FRAME_MAX not supported yet\n");
877         else
878             FIXME("MCI_DGV_WHERE_FRAME not supported yet\n");
879         goto out;
880     }
881     if (dwFlags & MCI_DGV_WHERE_VIDEO) {
882         if (dwFlags & MCI_DGV_WHERE_MAX)
883             FIXME("MCI_DGV_WHERE_VIDEO_MAX not supported yet\n");
884         else
885             FIXME("MCI_DGV_WHERE_VIDEO not supported yet\n");
886         goto out;
887     }
888     if (dwFlags & MCI_DGV_WHERE_WINDOW) {
889         if (dwFlags & MCI_DGV_WHERE_MAX) {
890             GetWindowRect(GetDesktopWindow(), &rc);
891             rc.right -= rc.left;
892             rc.bottom -= rc.top;
893             TRACE("MCI_DGV_WHERE_WINDOW_MAX %s\n", wine_dbgstr_rect(&rc));
894         } else {
895             IVideoWindow_GetWindowPosition(wma->vidwin, &rc.left, &rc.top, &rc.right, &rc.bottom);
896             TRACE("MCI_DGV_WHERE_WINDOW %s\n", wine_dbgstr_rect(&rc));
897         }
898     }
899     ret = 0;
900 out:
901     lpParms->rc = rc;
902     return ret;
903 }
904 
905 /***************************************************************************
906  *                              MCIQTZ_mciWindow                [internal]
907  */
908 static DWORD MCIQTZ_mciWindow(UINT wDevID, DWORD dwFlags, LPMCI_DGV_WINDOW_PARMSW lpParms)
909 {
910     WINE_MCIQTZ *wma = MCIQTZ_mciGetOpenDev(wDevID);
911 
912     TRACE("(%04x, %08X, %p)\n", wDevID, dwFlags, lpParms);
913 
914     if(!lpParms)
915         return MCIERR_NULL_PARAMETER_BLOCK;
916 
917     if (!wma)
918         return MCIERR_INVALID_DEVICE_ID;
919     if (dwFlags & MCI_TEST)
920         return 0;
921 
922     if (dwFlags & MCI_DGV_WINDOW_HWND && (IsWindow(lpParms->hWnd) || !lpParms->hWnd)) {
923         LONG visible = OATRUE;
924         LONG style = 0;
925         TRACE("Setting hWnd to %p\n", lpParms->hWnd);
926         IVideoWindow_get_Visible(wma->vidwin, &visible);
927         IVideoWindow_put_Visible(wma->vidwin, OAFALSE);
928         IVideoWindow_get_WindowStyle(wma->vidwin, &style);
929         style &= ~WS_CHILD;
930         if (lpParms->hWnd)
931             IVideoWindow_put_WindowStyle(wma->vidwin, style|WS_CHILD);
932         else
933             IVideoWindow_put_WindowStyle(wma->vidwin, style);
934         IVideoWindow_put_Owner(wma->vidwin, (OAHWND)lpParms->hWnd);
935         IVideoWindow_put_MessageDrain(wma->vidwin, (OAHWND)lpParms->hWnd);
936         IVideoWindow_put_Visible(wma->vidwin, visible);
937         wma->parent = lpParms->hWnd;
938     }
939     if (dwFlags & MCI_DGV_WINDOW_STATE) {
940         TRACE("Setting nCmdShow to %d\n", lpParms->nCmdShow);
941         IVideoWindow_put_WindowState(wma->vidwin, lpParms->nCmdShow);
942     }
943     if (dwFlags & MCI_DGV_WINDOW_TEXT) {
944         TRACE("Setting caption to %s\n", debugstr_w(lpParms->lpstrText));
945         IVideoWindow_put_Caption(wma->vidwin, lpParms->lpstrText);
946     }
947     return 0;
948 }
949 
950 /***************************************************************************
951  *                              MCIQTZ_mciPut                   [internal]
952  */
953 static DWORD MCIQTZ_mciPut(UINT wDevID, DWORD dwFlags, MCI_GENERIC_PARMS *lpParms)
954 {
955     WINE_MCIQTZ *wma = MCIQTZ_mciGetOpenDev(wDevID);
956     MCI_DGV_RECT_PARMS *rectparms;
957     HRESULT hr;
958 
959     TRACE("(%04x, %08X, %p)\n", wDevID, dwFlags, lpParms);
960 
961     if(!lpParms)
962         return MCIERR_NULL_PARAMETER_BLOCK;
963 
964     if (!wma)
965         return MCIERR_INVALID_DEVICE_ID;
966 
967     if (!(dwFlags & MCI_DGV_RECT)) {
968         FIXME("No support for non-RECT MCI_PUT\n");
969         return 1;
970     }
971 
972     if (dwFlags & MCI_TEST)
973         return 0;
974 
975     dwFlags &= ~MCI_DGV_RECT;
976     rectparms = (MCI_DGV_RECT_PARMS*)lpParms;
977 
978     if (dwFlags & MCI_DGV_PUT_DESTINATION) {
979         hr = IVideoWindow_SetWindowPosition(wma->vidwin,
980                 rectparms->rc.left, rectparms->rc.top,
981                 rectparms->rc.right - rectparms->rc.left,
982                 rectparms->rc.bottom - rectparms->rc.top);
983         if(FAILED(hr))
984             WARN("IVideoWindow_SetWindowPosition failed: 0x%x\n", hr);
985 
986         dwFlags &= ~MCI_DGV_PUT_DESTINATION;
987     }
988 
989     if (dwFlags & MCI_NOTIFY) {
990         MCIQTZ_mciNotify(lpParms->dwCallback, wma, MCI_NOTIFY_SUCCESSFUL);
991         dwFlags &= ~MCI_NOTIFY;
992     }
993 
994     if (dwFlags)
995         FIXME("No support for some flags: 0x%x\n", dwFlags);
996 
997     return 0;
998 }
999 
1000 /******************************************************************************
1001  *              MCIAVI_mciUpdate            [internal]
1002  */
1003 static DWORD MCIQTZ_mciUpdate(UINT wDevID, DWORD dwFlags, LPMCI_DGV_UPDATE_PARMS lpParms)
1004 {
1005     WINE_MCIQTZ *wma;
1006     DWORD res = 0;
1007 
1008     TRACE("%04x, %08x, %p\n", wDevID, dwFlags, lpParms);
1009 
1010     if(!lpParms)
1011         return MCIERR_NULL_PARAMETER_BLOCK;
1012 
1013     wma = MCIQTZ_mciGetOpenDev(wDevID);
1014     if (!wma)
1015         return MCIERR_INVALID_DEVICE_ID;
1016 
1017     if (dwFlags & MCI_DGV_UPDATE_HDC) {
1018         LONG state, size;
1019         BYTE *data;
1020         BITMAPINFO *info;
1021         HRESULT hr;
1022         RECT src, dest;
1023         LONG visible = OATRUE;
1024 
1025         res = MCIERR_INTERNAL;
1026         IMediaControl_GetState(wma->pmctrl, -1, &state);
1027         if (state == State_Running)
1028             return MCIERR_UNSUPPORTED_FUNCTION;
1029         /* If in stopped state, nothing has been drawn to screen
1030          * moving to pause, which is needed for the old dib renderer, will result
1031          * in a single frame drawn, so hide the window here */
1032         IVideoWindow_get_Visible(wma->vidwin, &visible);
1033         if (wma->parent)
1034             IVideoWindow_put_Visible(wma->vidwin, OAFALSE);
1035         /* FIXME: Should we check the original state and restore it? */
1036         IMediaControl_Pause(wma->pmctrl);
1037         IMediaControl_GetState(wma->pmctrl, -1, &state);
1038         if (FAILED(hr = IBasicVideo_GetCurrentImage(wma->vidbasic, &size, NULL))) {
1039             WARN("Could not get image size (hr = %x)\n", hr);
1040             goto out;
1041         }
1042         data = HeapAlloc(GetProcessHeap(), 0, size);
1043         info = (BITMAPINFO*)data;
1044         IBasicVideo_GetCurrentImage(wma->vidbasic, &size, (LONG*)data);
1045         data += info->bmiHeader.biSize;
1046 
1047         IBasicVideo_GetSourcePosition(wma->vidbasic, &src.left, &src.top, &src.right, &src.bottom);
1048         IBasicVideo_GetDestinationPosition(wma->vidbasic, &dest.left, &dest.top, &dest.right, &dest.bottom);
1049         StretchDIBits(lpParms->hDC,
1050               dest.left, dest.top, dest.right + dest.left, dest.bottom + dest.top,
1051               src.left, src.top, src.right + src.left, src.bottom + src.top,
1052               data, info, DIB_RGB_COLORS, SRCCOPY);
1053         HeapFree(GetProcessHeap(), 0, data);
1054         res = 0;
1055 out:
1056         if (wma->parent)
1057             IVideoWindow_put_Visible(wma->vidwin, visible);
1058     }
1059     else if (dwFlags)
1060         FIXME("Unhandled flags %x\n", dwFlags);
1061     return res;
1062 }
1063 
1064 /***************************************************************************
1065  *                              MCIQTZ_mciSetAudio              [internal]
1066  */
1067 static DWORD MCIQTZ_mciSetAudio(UINT wDevID, DWORD dwFlags, LPMCI_DGV_SETAUDIO_PARMSW lpParms)
1068 {
1069     WINE_MCIQTZ *wma;
1070     DWORD ret = 0;
1071 
1072     TRACE("(%04x, %08x, %p)\n", wDevID, dwFlags, lpParms);
1073 
1074     if(!lpParms)
1075         return MCIERR_NULL_PARAMETER_BLOCK;
1076 
1077     wma = MCIQTZ_mciGetOpenDev(wDevID);
1078     if (!wma)
1079         return MCIERR_INVALID_DEVICE_ID;
1080 
1081     if (!(dwFlags & MCI_DGV_SETAUDIO_ITEM)) {
1082         FIXME("Unknown flags (%08x)\n", dwFlags);
1083         return 0;
1084     }
1085 
1086     if (dwFlags & MCI_DGV_SETAUDIO_ITEM) {
1087         switch (lpParms->dwItem) {
1088         case MCI_DGV_SETAUDIO_VOLUME:
1089             if (dwFlags & MCI_DGV_SETAUDIO_VALUE) {
1090                 long vol;
1091                 HRESULT hr;
1092                 if (lpParms->dwValue > 1000) {
1093                     ret = MCIERR_OUTOFRANGE;
1094                     break;
1095                 }
1096                 if (dwFlags & MCI_TEST)
1097                     break;
1098                 if (lpParms->dwValue != 0)
1099                     vol = (long)(2000.0 * (log10(lpParms->dwValue) - 3.0));
1100                 else
1101                     vol = -10000;
1102                 TRACE("Setting volume to %ld\n", vol);
1103                 hr = IBasicAudio_put_Volume(wma->audio, vol);
1104                 if (FAILED(hr)) {
1105                     WARN("Cannot set volume (hr = %x)\n", hr);
1106                     ret = MCIERR_INTERNAL;
1107                 }
1108             }
1109             break;
1110         default:
1111             FIXME("Unknown item %08x\n", lpParms->dwItem);
1112             break;
1113         }
1114     }
1115 
1116     return ret;
1117 }
1118 
1119 /*======================================================================*
1120  *                          MCI QTZ entry points                        *
1121  *======================================================================*/
1122 
1123 /**************************************************************************
1124  *                              DriverProc (MCIQTZ.@)
1125  */
1126 LRESULT CALLBACK MCIQTZ_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg,
1127                                    LPARAM dwParam1, LPARAM dwParam2)
1128 {
1129     TRACE("(%08lX, %p, %08X, %08lX, %08lX)\n",
1130           dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1131 
1132     switch (wMsg) {
1133         case DRV_LOAD:                  return 1;
1134         case DRV_FREE:                  return 1;
1135         case DRV_OPEN:                  return MCIQTZ_drvOpen((LPCWSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSW)dwParam2);
1136         case DRV_CLOSE:                 return MCIQTZ_drvClose(dwDevID);
1137         case DRV_ENABLE:                return 1;
1138         case DRV_DISABLE:               return 1;
1139         case DRV_QUERYCONFIGURE:        return 1;
1140         case DRV_CONFIGURE:             return MCIQTZ_drvConfigure(dwDevID);
1141         case DRV_INSTALL:               return DRVCNF_RESTART;
1142         case DRV_REMOVE:                return DRVCNF_RESTART;
1143     }
1144 
1145     /* session instance */
1146     if (dwDevID == 0xFFFFFFFF)
1147         return 1;
1148 
1149     switch (wMsg) {
1150         case MCI_OPEN_DRIVER:   return MCIQTZ_mciOpen      (dwDevID, dwParam1, (LPMCI_DGV_OPEN_PARMSW)     dwParam2);
1151         case MCI_CLOSE_DRIVER:  return MCIQTZ_mciClose     (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)       dwParam2);
1152         case MCI_PLAY:          return MCIQTZ_mciPlay      (dwDevID, dwParam1, (LPMCI_PLAY_PARMS)          dwParam2);
1153         case MCI_SEEK:          return MCIQTZ_mciSeek      (dwDevID, dwParam1, (LPMCI_SEEK_PARMS)          dwParam2);
1154         case MCI_STOP:          return MCIQTZ_mciStop      (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)       dwParam2);
1155         case MCI_PAUSE:         return MCIQTZ_mciPause     (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)       dwParam2);
1156         case MCI_RESUME:        return MCIQTZ_mciResume    (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)       dwParam2);
1157         case MCI_GETDEVCAPS:    return MCIQTZ_mciGetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS)    dwParam2);
1158         case MCI_SET:           return MCIQTZ_mciSet       (dwDevID, dwParam1, (LPMCI_DGV_SET_PARMS)       dwParam2);
1159         case MCI_STATUS:        return MCIQTZ_mciStatus    (dwDevID, dwParam1, (LPMCI_DGV_STATUS_PARMSW)   dwParam2);
1160         case MCI_WHERE:         return MCIQTZ_mciWhere     (dwDevID, dwParam1, (LPMCI_DGV_RECT_PARMS)      dwParam2);
1161         /* Digital Video specific */
1162         case MCI_SETAUDIO:      return MCIQTZ_mciSetAudio  (dwDevID, dwParam1, (LPMCI_DGV_SETAUDIO_PARMSW) dwParam2);
1163         case MCI_UPDATE:
1164             return MCIQTZ_mciUpdate(dwDevID, dwParam1, (LPMCI_DGV_UPDATE_PARMS)dwParam2);
1165         case MCI_WINDOW:
1166             return MCIQTZ_mciWindow(dwDevID, dwParam1, (LPMCI_DGV_WINDOW_PARMSW)dwParam2);
1167         case MCI_PUT:
1168             return MCIQTZ_mciPut(dwDevID, dwParam1, (MCI_GENERIC_PARMS*)dwParam2);
1169         case MCI_RECORD:
1170         case MCI_INFO:
1171         case MCI_LOAD:
1172         case MCI_SAVE:
1173         case MCI_FREEZE:
1174         case MCI_REALIZE:
1175         case MCI_UNFREEZE:
1176         case MCI_STEP:
1177         case MCI_COPY:
1178         case MCI_CUT:
1179         case MCI_DELETE:
1180         case MCI_PASTE:
1181         case MCI_CUE:
1182         /* Digital Video specific */
1183         case MCI_CAPTURE:
1184         case MCI_MONITOR:
1185         case MCI_RESERVE:
1186         case MCI_SIGNAL:
1187         case MCI_SETVIDEO:
1188         case MCI_QUALITY:
1189         case MCI_LIST:
1190         case MCI_UNDO:
1191         case MCI_CONFIGURE:
1192         case MCI_RESTORE:
1193             FIXME("Unimplemented command [%08X]\n", wMsg);
1194             break;
1195         case MCI_SPIN:
1196         case MCI_ESCAPE:
1197             WARN("Unsupported command [%08X]\n", wMsg);
1198             break;
1199         case MCI_OPEN:
1200         case MCI_CLOSE:
1201             FIXME("Shouldn't receive a MCI_OPEN or CLOSE message\n");
1202             break;
1203         default:
1204             TRACE("Sending msg [%08X] to default driver proc\n", wMsg);
1205             return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1206     }
1207 
1208     return MCIERR_UNRECOGNIZED_COMMAND;
1209 }
1210