imm32: Sign-compare warnings fix.
[wine] / dlls / quartz / control.c
1 /*
2  * Filter Seeking and Control Interfaces
3  *
4  * Copyright 2003 Robert Shearman
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 /* FIXME: critical sections */
21
22 #include "quartz_private.h"
23 #include "control_private.h"
24
25 #include "uuids.h"
26 #include "wine/debug.h"
27
28 #include <assert.h>
29
30 WINE_DEFAULT_DEBUG_CHANNEL(quartz);
31
32 static const IMediaSeekingVtbl IMediaSeekingPassThru_Vtbl;
33
34 typedef struct PassThruImpl {
35     const ISeekingPassThruVtbl *IPassThru_vtbl;
36     const IUnknownVtbl *IInner_vtbl;
37     const IMediaSeekingVtbl *IMediaSeeking_vtbl;
38
39     LONG ref;
40     IUnknown * pUnkOuter;
41     IPin * pin;
42     BOOL bUnkOuterValid;
43     BOOL bAggregatable;
44     BOOL renderer;
45 } PassThruImpl;
46
47 static HRESULT WINAPI SeekInner_QueryInterface(IUnknown * iface,
48                                           REFIID riid,
49                                           LPVOID *ppvObj) {
50     ICOM_THIS_MULTI(PassThruImpl, IInner_vtbl, iface);
51     TRACE("(%p)->(%s (%p), %p)\n", This, debugstr_guid(riid), riid, ppvObj);
52
53     if (This->bAggregatable)
54         This->bUnkOuterValid = TRUE;
55
56     if (IsEqualGUID(&IID_IUnknown, riid))
57     {
58         *ppvObj = &(This->IInner_vtbl);
59         TRACE("   returning IUnknown interface (%p)\n", *ppvObj);
60     } else if (IsEqualGUID(&IID_ISeekingPassThru, riid)) {
61         *ppvObj = &(This->IPassThru_vtbl);
62         TRACE("   returning ISeekingPassThru interface (%p)\n", *ppvObj);
63     } else if (IsEqualGUID(&IID_IMediaSeeking, riid)) {
64         *ppvObj = &(This->IMediaSeeking_vtbl);
65         TRACE("   returning IMediaSeeking interface (%p)\n", *ppvObj);
66     } else {
67         *ppvObj = NULL;
68         FIXME("unknown interface %s\n", debugstr_guid(riid));
69         return E_NOINTERFACE;
70     }
71
72     IUnknown_AddRef((IUnknown *)(*ppvObj));
73     return S_OK;
74 }
75
76 static ULONG WINAPI SeekInner_AddRef(IUnknown * iface) {
77     ICOM_THIS_MULTI(PassThruImpl, IInner_vtbl, iface);
78     ULONG ref = InterlockedIncrement(&This->ref);
79
80     TRACE("(%p)->(): new ref = %d\n", This, ref);
81
82     return ref;
83 }
84
85 static ULONG WINAPI SeekInner_Release(IUnknown * iface) {
86     ICOM_THIS_MULTI(PassThruImpl, IInner_vtbl, iface);
87     ULONG ref = InterlockedDecrement(&This->ref);
88
89     TRACE("(%p)->(): new ref = %d\n", This, ref);
90
91     if (ref == 0)
92     {
93         CoTaskMemFree(This);
94     }
95     return ref;
96 }
97
98 static const IUnknownVtbl IInner_VTable =
99 {
100     SeekInner_QueryInterface,
101     SeekInner_AddRef,
102     SeekInner_Release
103 };
104
105 /* Generic functions for aggregation */
106 static HRESULT WINAPI SeekOuter_QueryInterface(PassThruImpl *This, REFIID riid, LPVOID *ppv)
107 {
108     if (This->bAggregatable)
109         This->bUnkOuterValid = TRUE;
110
111     if (This->pUnkOuter)
112     {
113         if (This->bAggregatable)
114             return IUnknown_QueryInterface(This->pUnkOuter, riid, ppv);
115
116         if (IsEqualIID(riid, &IID_IUnknown))
117         {
118             HRESULT hr;
119
120             IUnknown_AddRef((IUnknown *)&(This->IInner_vtbl));
121             hr = IUnknown_QueryInterface((IUnknown *)&(This->IInner_vtbl), riid, ppv);
122             IUnknown_Release((IUnknown *)&(This->IInner_vtbl));
123             This->bAggregatable = TRUE;
124             return hr;
125         }
126
127         *ppv = NULL;
128         return E_NOINTERFACE;
129     }
130
131     return IUnknown_QueryInterface((IUnknown *)&(This->IInner_vtbl), riid, ppv);
132 }
133
134 static ULONG WINAPI SeekOuter_AddRef(PassThruImpl *This)
135 {
136     if (This->pUnkOuter && This->bUnkOuterValid)
137         return IUnknown_AddRef(This->pUnkOuter);
138     return IUnknown_AddRef((IUnknown *)&(This->IInner_vtbl));
139 }
140
141 static ULONG WINAPI SeekOuter_Release(PassThruImpl *This)
142 {
143     if (This->pUnkOuter && This->bUnkOuterValid)
144         return IUnknown_Release(This->pUnkOuter);
145     return IUnknown_Release((IUnknown *)&(This->IInner_vtbl));
146 }
147
148 static HRESULT WINAPI SeekingPassThru_QueryInterface(ISeekingPassThru *iface, REFIID riid, LPVOID *ppvObj)
149 {
150     ICOM_THIS_MULTI(PassThruImpl, IPassThru_vtbl, iface);
151
152     TRACE("(%p/%p)->(%s (%p), %p)\n", This, iface, debugstr_guid(riid), riid, ppvObj);
153
154     return SeekOuter_QueryInterface(This, riid, ppvObj);
155 }
156
157 static ULONG WINAPI SeekingPassThru_AddRef(ISeekingPassThru *iface)
158 {
159     ICOM_THIS_MULTI(PassThruImpl, IPassThru_vtbl, iface);
160
161     TRACE("(%p/%p)->()\n", This, iface);
162
163     return SeekOuter_AddRef(This);
164 }
165
166 static ULONG WINAPI SeekingPassThru_Release(ISeekingPassThru *iface)
167 {
168     ICOM_THIS_MULTI(PassThruImpl, IPassThru_vtbl, iface);
169
170     TRACE("(%p/%p)->()\n", This, iface);
171
172     return SeekOuter_Release(This);
173 }
174
175 static HRESULT WINAPI SeekingPassThru_Init(ISeekingPassThru *iface, BOOL renderer, IPin *pin)
176 {
177     ICOM_THIS_MULTI(PassThruImpl, IPassThru_vtbl, iface);
178
179     TRACE("(%p/%p)->(%d, %p)\n", This, iface, renderer, pin);
180
181     if (This->pin)
182         FIXME("Re-initializing?\n");
183
184     This->renderer = renderer;
185     This->pin = pin;
186
187     return S_OK;
188 }
189
190 static const ISeekingPassThruVtbl ISeekingPassThru_Vtbl =
191 {
192     SeekingPassThru_QueryInterface,
193     SeekingPassThru_AddRef,
194     SeekingPassThru_Release,
195     SeekingPassThru_Init
196 };
197
198 HRESULT SeekingPassThru_create(IUnknown *pUnkOuter, LPVOID *ppObj)
199 {
200     PassThruImpl *fimpl;
201
202     TRACE("(%p,%p)\n", pUnkOuter, ppObj);
203
204     *ppObj = fimpl = CoTaskMemAlloc(sizeof(*fimpl));
205     if (!fimpl)
206         return E_OUTOFMEMORY;
207
208     fimpl->pUnkOuter = pUnkOuter;
209     fimpl->bUnkOuterValid = FALSE;
210     fimpl->bAggregatable = FALSE;
211     fimpl->IInner_vtbl = &IInner_VTable;
212     fimpl->IPassThru_vtbl = &ISeekingPassThru_Vtbl;
213     fimpl->IMediaSeeking_vtbl = &IMediaSeekingPassThru_Vtbl;
214     fimpl->ref = 1;
215     fimpl->pin = NULL;
216     return S_OK;
217 }
218
219 typedef HRESULT (*SeekFunc)( IMediaSeeking *to, LPVOID arg );
220
221 static HRESULT ForwardCmdSeek( PCRITICAL_SECTION crit_sect, IBaseFilter* from, SeekFunc fnSeek, LPVOID arg )
222 {
223     HRESULT hr = S_OK;
224     HRESULT hr_return = S_OK;
225     IEnumPins *enumpins = NULL;
226     BOOL foundend = FALSE, allnotimpl = TRUE;
227
228     hr = IBaseFilter_EnumPins( from, &enumpins );
229     if (FAILED(hr))
230         goto out;
231
232     hr = IEnumPins_Reset( enumpins );
233     while (hr == S_OK) {
234         IPin *pin = NULL;
235         hr = IEnumPins_Next( enumpins, 1, &pin, NULL );
236         if (hr == VFW_E_ENUM_OUT_OF_SYNC)
237         {
238             hr = IEnumPins_Reset( enumpins );
239             continue;
240         }
241         if (pin)
242         {
243             PIN_DIRECTION dir;
244
245             IPin_QueryDirection( pin, &dir );
246             if (dir == PINDIR_INPUT)
247             {
248                 IPin *connected = NULL;
249
250                 IPin_ConnectedTo( pin, &connected );
251                 if (connected)
252                 {
253                     HRESULT hr_local;
254                     IMediaSeeking *seek = NULL;
255
256                     hr_local = IPin_QueryInterface( connected, &IID_IMediaSeeking, (void**)&seek );
257                     if (hr_local == S_OK)
258                     {
259                         foundend = TRUE;
260                         if (crit_sect)
261                         {
262                             LeaveCriticalSection( crit_sect );
263                             hr_local = fnSeek( seek , arg );
264                             EnterCriticalSection( crit_sect );
265                         }
266                         else
267                             hr_local = fnSeek( seek , arg );
268
269                         if (hr_local != E_NOTIMPL)
270                             allnotimpl = FALSE;
271
272                         hr_return = updatehres( hr_return, hr_local );
273                         IMediaSeeking_Release( seek );
274                     }
275                     IPin_Release(connected);
276                 }
277             }
278             IPin_Release( pin );
279         }
280     }
281     if (foundend && allnotimpl)
282         hr = E_NOTIMPL;
283     else
284         hr = hr_return;
285
286 out:
287     TRACE("Returning: %08x\n", hr);
288     return hr;
289 }
290
291
292 HRESULT MediaSeekingImpl_Init(IBaseFilter *pUserData, CHANGEPROC fnChangeStop, CHANGEPROC fnChangeCurrent, CHANGEPROC fnChangeRate, MediaSeekingImpl * pSeeking, PCRITICAL_SECTION crit_sect)
293 {
294     assert(fnChangeStop && fnChangeCurrent && fnChangeRate);
295
296     pSeeking->refCount = 1;
297     pSeeking->pUserData = pUserData;
298     pSeeking->fnChangeRate = fnChangeRate;
299     pSeeking->fnChangeStop = fnChangeStop;
300     pSeeking->fnChangeCurrent = fnChangeCurrent;
301     pSeeking->dwCapabilities = AM_SEEKING_CanSeekForwards |
302         AM_SEEKING_CanSeekBackwards |
303         AM_SEEKING_CanSeekAbsolute |
304         AM_SEEKING_CanGetStopPos |
305         AM_SEEKING_CanGetDuration;
306     pSeeking->llCurrent = 0;
307     pSeeking->llStop = ((ULONGLONG)0x80000000) << 32;
308     pSeeking->llDuration = pSeeking->llStop;
309     pSeeking->dRate = 1.0;
310     pSeeking->timeformat = TIME_FORMAT_MEDIA_TIME;
311     pSeeking->crst = crit_sect;
312
313     return S_OK;
314 }
315
316 struct pos_args {
317     LONGLONG* current, *stop;
318     DWORD curflags, stopflags;
319 };
320
321 static HRESULT fwd_setposition(IMediaSeeking *seek, LPVOID pargs)
322 {
323     struct pos_args *args = (void*)pargs;
324
325     return IMediaSeeking_SetPositions(seek, args->current, args->curflags, args->stop, args->stopflags);
326 }
327
328 static HRESULT fwd_checkcaps(IMediaSeeking *iface, LPVOID pcaps)
329 {
330     DWORD *caps = pcaps;
331     return IMediaSeeking_CheckCapabilities(iface, caps);
332 }
333
334 static HRESULT fwd_settimeformat(IMediaSeeking *iface, LPVOID pformat)
335 {
336     const GUID *format = pformat;
337     return IMediaSeeking_SetTimeFormat(iface, format);
338 }
339
340 static HRESULT fwd_getduration(IMediaSeeking *iface, LPVOID pdur)
341 {
342     LONGLONG *duration = pdur;
343     LONGLONG mydur = *duration;
344     HRESULT hr;
345
346     hr = IMediaSeeking_GetDuration(iface, &mydur);
347     if (FAILED(hr))
348         return hr;
349
350     if ((mydur < *duration) || (*duration < 0 && mydur > 0))
351         *duration = mydur;
352     return hr;
353 }
354
355 static HRESULT fwd_getstopposition(IMediaSeeking *iface, LPVOID pdur)
356 {
357     LONGLONG *duration = pdur;
358     LONGLONG mydur = *duration;
359     HRESULT hr;
360
361     hr = IMediaSeeking_GetStopPosition(iface, &mydur);
362     if (FAILED(hr))
363         return hr;
364
365     if ((mydur < *duration) || (*duration < 0 && mydur > 0))
366         *duration = mydur;
367     return hr;
368 }
369
370 static HRESULT fwd_getcurposition(IMediaSeeking *iface, LPVOID pdur)
371 {
372     LONGLONG *duration = pdur;
373     LONGLONG mydur = *duration;
374     HRESULT hr;
375
376     hr = IMediaSeeking_GetCurrentPosition(iface, &mydur);
377     if (FAILED(hr))
378         return hr;
379
380     if ((mydur < *duration) || (*duration < 0 && mydur > 0))
381         *duration = mydur;
382     return hr;
383 }
384
385 static HRESULT fwd_setrate(IMediaSeeking *iface, LPVOID prate)
386 {
387     double *rate = prate;
388
389     HRESULT hr;
390
391     hr = IMediaSeeking_SetRate(iface, *rate);
392     if (FAILED(hr))
393         return hr;
394
395     return hr;
396 }
397
398
399 HRESULT WINAPI MediaSeekingImpl_GetCapabilities(IMediaSeeking * iface, DWORD * pCapabilities)
400 {
401     MediaSeekingImpl *This = (MediaSeekingImpl *)iface;
402
403     TRACE("(%p)\n", pCapabilities);
404
405     *pCapabilities = This->dwCapabilities;
406
407     return S_OK;
408 }
409
410 HRESULT WINAPI MediaSeekingImpl_CheckCapabilities(IMediaSeeking * iface, DWORD * pCapabilities)
411 {
412     MediaSeekingImpl *This = (MediaSeekingImpl *)iface;
413     HRESULT hr;
414     DWORD dwCommonCaps;
415
416     TRACE("(%p)\n", pCapabilities);
417
418     if (!pCapabilities)
419         return E_POINTER;
420
421     EnterCriticalSection(This->crst);
422     hr = ForwardCmdSeek(This->crst, This->pUserData, fwd_checkcaps, pCapabilities);
423     LeaveCriticalSection(This->crst);
424     if (FAILED(hr) && hr != E_NOTIMPL)
425         return hr;
426
427     dwCommonCaps = *pCapabilities & This->dwCapabilities;
428
429     if (!dwCommonCaps)
430         hr = E_FAIL;
431     else
432         hr = (*pCapabilities == dwCommonCaps) ?  S_OK : S_FALSE;
433     *pCapabilities = dwCommonCaps;
434
435     return hr;
436 }
437
438 HRESULT WINAPI MediaSeekingImpl_IsFormatSupported(IMediaSeeking * iface, const GUID * pFormat)
439 {
440     TRACE("(%s)\n", qzdebugstr_guid(pFormat));
441
442     return (IsEqualIID(pFormat, &TIME_FORMAT_MEDIA_TIME) ? S_OK : S_FALSE);
443 }
444
445 HRESULT WINAPI MediaSeekingImpl_QueryPreferredFormat(IMediaSeeking * iface, GUID * pFormat)
446 {
447     TRACE("(%s)\n", qzdebugstr_guid(pFormat));
448
449     *pFormat = TIME_FORMAT_MEDIA_TIME;
450     return S_OK;
451 }
452
453 HRESULT WINAPI MediaSeekingImpl_GetTimeFormat(IMediaSeeking * iface, GUID * pFormat)
454 {
455     MediaSeekingImpl *This = (MediaSeekingImpl *)iface;
456     TRACE("(%s)\n", qzdebugstr_guid(pFormat));
457
458     EnterCriticalSection(This->crst);
459     *pFormat = This->timeformat;
460     LeaveCriticalSection(This->crst);
461
462     return S_OK;
463 }
464
465 HRESULT WINAPI MediaSeekingImpl_IsUsingTimeFormat(IMediaSeeking * iface, const GUID * pFormat)
466 {
467     MediaSeekingImpl *This = (MediaSeekingImpl *)iface;
468     HRESULT hr = S_OK;
469
470     TRACE("(%s)\n", qzdebugstr_guid(pFormat));
471
472     EnterCriticalSection(This->crst);
473     if (!IsEqualIID(pFormat, &This->timeformat))
474         hr = S_FALSE;
475     LeaveCriticalSection(This->crst);
476
477     return hr;
478 }
479
480
481 HRESULT WINAPI MediaSeekingImpl_SetTimeFormat(IMediaSeeking * iface, const GUID * pFormat)
482 {
483     MediaSeekingImpl *This = (MediaSeekingImpl *)iface;
484     TRACE("(%s)\n", qzdebugstr_guid(pFormat));
485
486     EnterCriticalSection(This->crst);
487     ForwardCmdSeek(This->crst, This->pUserData, fwd_settimeformat, (LPVOID)pFormat);
488     LeaveCriticalSection(This->crst);
489
490     return (IsEqualIID(pFormat, &TIME_FORMAT_MEDIA_TIME) ? S_OK : S_FALSE);
491 }
492
493
494 HRESULT WINAPI MediaSeekingImpl_GetDuration(IMediaSeeking * iface, LONGLONG * pDuration)
495 {
496     MediaSeekingImpl *This = (MediaSeekingImpl *)iface;
497
498     TRACE("(%p)\n", pDuration);
499
500     EnterCriticalSection(This->crst);
501     *pDuration = This->llDuration;
502     ForwardCmdSeek(This->crst, This->pUserData, fwd_getduration, pDuration);
503     LeaveCriticalSection(This->crst);
504
505     return S_OK;
506 }
507
508 HRESULT WINAPI MediaSeekingImpl_GetStopPosition(IMediaSeeking * iface, LONGLONG * pStop)
509 {
510     MediaSeekingImpl *This = (MediaSeekingImpl *)iface;
511
512     TRACE("(%p)\n", pStop);
513
514     EnterCriticalSection(This->crst);
515     *pStop = This->llStop;
516     ForwardCmdSeek(This->crst, This->pUserData, fwd_getstopposition, pStop);
517     LeaveCriticalSection(This->crst);
518
519     return S_OK;
520 }
521
522 /* FIXME: Make use of the info the filter should expose */
523 HRESULT WINAPI MediaSeekingImpl_GetCurrentPosition(IMediaSeeking * iface, LONGLONG * pCurrent)
524 {
525     MediaSeekingImpl *This = (MediaSeekingImpl *)iface;
526
527     TRACE("(%p)\n", pCurrent);
528
529     EnterCriticalSection(This->crst);
530     *pCurrent = This->llCurrent;
531     ForwardCmdSeek(This->crst, This->pUserData, fwd_getcurposition, pCurrent);
532     LeaveCriticalSection(This->crst);
533
534     return S_OK;
535 }
536
537 HRESULT WINAPI MediaSeekingImpl_ConvertTimeFormat(IMediaSeeking * iface, LONGLONG * pTarget, const GUID * pTargetFormat, LONGLONG Source, const GUID * pSourceFormat)
538 {
539     if (IsEqualIID(pTargetFormat, &TIME_FORMAT_MEDIA_TIME) && IsEqualIID(pSourceFormat, &TIME_FORMAT_MEDIA_TIME))
540     {
541         *pTarget = Source;
542         return S_OK;
543     }
544     /* FIXME: clear pTarget? */
545     return E_INVALIDARG;
546 }
547
548 static inline LONGLONG Adjust(LONGLONG value, const LONGLONG * pModifier, DWORD dwFlags)
549 {
550     switch (dwFlags & AM_SEEKING_PositioningBitsMask)
551     {
552     case AM_SEEKING_NoPositioning:
553         return value;
554     case AM_SEEKING_AbsolutePositioning:
555         return *pModifier;
556     case AM_SEEKING_RelativePositioning:
557     case AM_SEEKING_IncrementalPositioning:
558         return value + *pModifier;
559     default:
560         assert(FALSE);
561         return 0;
562     }
563 }
564
565 HRESULT WINAPI MediaSeekingImpl_SetPositions(IMediaSeeking * iface, LONGLONG * pCurrent, DWORD dwCurrentFlags, LONGLONG * pStop, DWORD dwStopFlags)
566 {
567     MediaSeekingImpl *This = (MediaSeekingImpl *)iface;
568     BOOL bChangeCurrent = FALSE, bChangeStop = FALSE;
569     LONGLONG llNewCurrent, llNewStop;
570     struct pos_args args;
571
572     TRACE("(%p, %x, %p, %x)\n", pCurrent, dwCurrentFlags, pStop, dwStopFlags);
573
574     args.current = pCurrent;
575     args.stop = pStop;
576     args.curflags = dwCurrentFlags;
577     args.stopflags = dwStopFlags;
578
579     EnterCriticalSection(This->crst);
580
581     llNewCurrent = Adjust(This->llCurrent, pCurrent, dwCurrentFlags);
582     llNewStop = Adjust(This->llStop, pStop, dwStopFlags);
583
584     if (pCurrent)
585         bChangeCurrent = TRUE;
586     if (llNewStop != This->llStop)
587         bChangeStop = TRUE;
588
589     TRACE("Old: %u, New: %u\n", (DWORD)(This->llCurrent/10000000), (DWORD)(llNewCurrent/10000000));
590
591     This->llCurrent = llNewCurrent;
592     This->llStop = llNewStop;
593
594     if (dwCurrentFlags & AM_SEEKING_ReturnTime)
595         *pCurrent = llNewCurrent;
596     if (dwStopFlags & AM_SEEKING_ReturnTime)
597         *pStop = llNewStop;
598
599     ForwardCmdSeek(This->crst, This->pUserData, fwd_setposition, &args);
600     LeaveCriticalSection(This->crst);
601
602     if (bChangeCurrent)
603         This->fnChangeCurrent(This->pUserData);
604     if (bChangeStop)
605         This->fnChangeStop(This->pUserData);
606
607     return S_OK;
608 }
609
610 HRESULT WINAPI MediaSeekingImpl_GetPositions(IMediaSeeking * iface, LONGLONG * pCurrent, LONGLONG * pStop)
611 {
612     MediaSeekingImpl *This = (MediaSeekingImpl *)iface;
613
614     TRACE("(%p, %p)\n", pCurrent, pStop);
615
616     EnterCriticalSection(This->crst);
617     *pCurrent = This->llCurrent;
618     *pStop = This->llStop;
619     LeaveCriticalSection(This->crst);
620
621     return S_OK;
622 }
623
624 HRESULT WINAPI MediaSeekingImpl_GetAvailable(IMediaSeeking * iface, LONGLONG * pEarliest, LONGLONG * pLatest)
625 {
626     MediaSeekingImpl *This = (MediaSeekingImpl *)iface;
627
628     TRACE("(%p, %p)\n", pEarliest, pLatest);
629
630     EnterCriticalSection(This->crst);
631     *pEarliest = 0;
632     *pLatest = This->llDuration;
633     LeaveCriticalSection(This->crst);
634
635     return S_OK;
636 }
637
638 HRESULT WINAPI MediaSeekingImpl_SetRate(IMediaSeeking * iface, double dRate)
639 {
640     MediaSeekingImpl *This = (MediaSeekingImpl *)iface;
641     BOOL bChangeRate = (dRate != This->dRate);
642     HRESULT hr = S_OK;
643
644     TRACE("(%e)\n", dRate);
645
646     if (dRate > 100 || dRate < .001)
647     {
648         FIXME("Excessive rate %e, ignoring\n", dRate);
649         return VFW_E_UNSUPPORTED_AUDIO;
650     }
651
652     EnterCriticalSection(This->crst);
653     This->dRate = dRate;
654     if (bChangeRate)
655         hr = This->fnChangeRate(This->pUserData);
656     ForwardCmdSeek(This->crst, This->pUserData, fwd_setrate, &dRate);
657     LeaveCriticalSection(This->crst);
658
659     return hr;
660 }
661
662 HRESULT WINAPI MediaSeekingImpl_GetRate(IMediaSeeking * iface, double * dRate)
663 {
664     MediaSeekingImpl *This = (MediaSeekingImpl *)iface;
665
666     TRACE("(%p)\n", dRate);
667
668     EnterCriticalSection(This->crst);
669     /* Forward? */
670     *dRate = This->dRate;
671     LeaveCriticalSection(This->crst);
672
673     return S_OK;
674 }
675
676 HRESULT WINAPI MediaSeekingImpl_GetPreroll(IMediaSeeking * iface, LONGLONG * pPreroll)
677 {
678     TRACE("(%p)\n", pPreroll);
679
680     *pPreroll = 0;
681     return S_OK;
682 }
683
684 static HRESULT WINAPI MediaSeekingPassThru_QueryInterface(IMediaSeeking *iface, REFIID riid, LPVOID *ppvObj)
685 {
686     ICOM_THIS_MULTI(PassThruImpl, IMediaSeeking_vtbl, iface);
687
688     TRACE("(%p/%p)->(%s (%p), %p)\n", This, iface, debugstr_guid(riid), riid, ppvObj);
689
690     return SeekOuter_QueryInterface(This, riid, ppvObj);
691 }
692
693 static ULONG WINAPI MediaSeekingPassThru_AddRef(IMediaSeeking *iface)
694 {
695     ICOM_THIS_MULTI(PassThruImpl, IMediaSeeking_vtbl, iface);
696
697     TRACE("(%p/%p)->()\n", iface, This);
698
699     return SeekOuter_AddRef(This);
700 }
701
702 static ULONG WINAPI MediaSeekingPassThru_Release(IMediaSeeking *iface)
703 {
704     ICOM_THIS_MULTI(PassThruImpl, IMediaSeeking_vtbl, iface);
705
706     TRACE("(%p/%p)->()\n", iface, This);
707
708     return SeekOuter_Release(This);
709 }
710
711 static HRESULT WINAPI MediaSeekingPassThru_GetCapabilities(IMediaSeeking * iface, DWORD * pCapabilities)
712 {
713     ICOM_THIS_MULTI(PassThruImpl, IMediaSeeking_vtbl, iface);
714
715     TRACE("(%p/%p)->(%p)\n", iface, This, pCapabilities);
716
717     FIXME("stub\n");
718     return E_NOTIMPL;
719 }
720
721 static HRESULT WINAPI MediaSeekingPassThru_CheckCapabilities(IMediaSeeking * iface, DWORD * pCapabilities)
722 {
723     ICOM_THIS_MULTI(PassThruImpl, IMediaSeeking_vtbl, iface);
724
725     TRACE("(%p/%p)->(%p)\n", iface, This, pCapabilities);
726
727     if (!pCapabilities)
728         return E_POINTER;
729
730     FIXME("stub\n");
731     return E_NOTIMPL;
732 }
733
734 static HRESULT WINAPI MediaSeekingPassThru_IsFormatSupported(IMediaSeeking * iface, const GUID * pFormat)
735 {
736     ICOM_THIS_MULTI(PassThruImpl, IMediaSeeking_vtbl, iface);
737     TRACE("(%p/%p)->(%s)\n", iface, This, qzdebugstr_guid(pFormat));
738
739     FIXME("stub\n");
740     return E_NOTIMPL;
741 }
742
743 static HRESULT WINAPI MediaSeekingPassThru_QueryPreferredFormat(IMediaSeeking * iface, GUID * pFormat)
744 {
745     ICOM_THIS_MULTI(PassThruImpl, IMediaSeeking_vtbl, iface);
746     TRACE("(%p/%p)->(%p)\n", iface, This, pFormat);
747
748     FIXME("stub\n");
749     return E_NOTIMPL;
750 }
751
752 static HRESULT WINAPI MediaSeekingPassThru_GetTimeFormat(IMediaSeeking * iface, GUID * pFormat)
753 {
754     ICOM_THIS_MULTI(PassThruImpl, IMediaSeeking_vtbl, iface);
755     TRACE("(%p/%p)->(%p)\n", iface, This, pFormat);
756
757     FIXME("stub\n");
758     return E_NOTIMPL;
759 }
760
761 static HRESULT WINAPI MediaSeekingPassThru_IsUsingTimeFormat(IMediaSeeking * iface, const GUID * pFormat)
762 {
763     ICOM_THIS_MULTI(PassThruImpl, IMediaSeeking_vtbl, iface);
764
765     TRACE("(%p/%p)->(%s)\n", iface, This, qzdebugstr_guid(pFormat));
766
767     FIXME("stub\n");
768     return E_NOTIMPL;
769 }
770
771
772 static HRESULT WINAPI MediaSeekingPassThru_SetTimeFormat(IMediaSeeking * iface, const GUID * pFormat)
773 {
774     ICOM_THIS_MULTI(PassThruImpl, IMediaSeeking_vtbl, iface);
775     TRACE("(%p/%p)->(%s)\n", iface, This, qzdebugstr_guid(pFormat));
776
777     FIXME("stub\n");
778     return E_NOTIMPL;
779 }
780
781
782 static HRESULT WINAPI MediaSeekingPassThru_GetDuration(IMediaSeeking * iface, LONGLONG * pDuration)
783 {
784     ICOM_THIS_MULTI(PassThruImpl, IMediaSeeking_vtbl, iface);
785     PIN_INFO info;
786     HRESULT hr;
787
788     TRACE("(%p/%p)->(%p)\n", iface, This, pDuration);
789
790     IPin_QueryPinInfo(This->pin, &info);
791
792     hr = ForwardCmdSeek(NULL, info.pFilter, fwd_getduration, pDuration);
793     IBaseFilter_Release(info.pFilter);
794
795     return hr;
796 }
797
798 static HRESULT WINAPI MediaSeekingPassThru_GetStopPosition(IMediaSeeking * iface, LONGLONG * pStop)
799 {
800     ICOM_THIS_MULTI(PassThruImpl, IMediaSeeking_vtbl, iface);
801
802     TRACE("(%p/%p)->(%p)\n", iface, This, pStop);
803
804     FIXME("stub\n");
805     return E_NOTIMPL;
806 }
807
808 /* FIXME: Make use of the info the filter should expose */
809 static HRESULT WINAPI MediaSeekingPassThru_GetCurrentPosition(IMediaSeeking * iface, LONGLONG * pCurrent)
810 {
811     ICOM_THIS_MULTI(PassThruImpl, IMediaSeeking_vtbl, iface);
812
813     TRACE("(%p/%p)->(%p)\n", iface, This, pCurrent);
814
815     FIXME("stub\n");
816     return E_NOTIMPL;
817 }
818
819 static HRESULT WINAPI MediaSeekingPassThru_ConvertTimeFormat(IMediaSeeking * iface, LONGLONG * pTarget, const GUID * pTargetFormat, LONGLONG Source, const GUID * pSourceFormat)
820 {
821     ICOM_THIS_MULTI(PassThruImpl, IMediaSeeking_vtbl, iface);
822
823     TRACE("(%p/%p)->(%p,%s,%x%08x,%s)\n", iface, This, pTarget, debugstr_guid(pTargetFormat), (DWORD)(Source>>32), (DWORD)Source, debugstr_guid(pSourceFormat));
824
825     FIXME("stub\n");
826     return E_NOTIMPL;
827 }
828
829 static HRESULT WINAPI MediaSeekingPassThru_SetPositions(IMediaSeeking * iface, LONGLONG * pCurrent, DWORD dwCurrentFlags, LONGLONG * pStop, DWORD dwStopFlags)
830 {
831     ICOM_THIS_MULTI(PassThruImpl, IMediaSeeking_vtbl, iface);
832     struct pos_args args;
833     PIN_INFO info;
834     HRESULT hr;
835
836     TRACE("(%p/%p)->(%p, %p)\n", iface, This, pCurrent, pStop);
837     args.current = pCurrent;
838     args.stop = pStop;
839     args.curflags = dwCurrentFlags;
840     args.stopflags = dwStopFlags;
841
842     IPin_QueryPinInfo(This->pin, &info);
843
844     hr = ForwardCmdSeek(NULL, info.pFilter, fwd_setposition, &args);
845     IBaseFilter_Release(info.pFilter);
846     return hr;
847 }
848
849 static HRESULT WINAPI MediaSeekingPassThru_GetPositions(IMediaSeeking * iface, LONGLONG * pCurrent, LONGLONG * pStop)
850 {
851     ICOM_THIS_MULTI(PassThruImpl, IMediaSeeking_vtbl, iface);
852
853     TRACE("(%p/%p)->(%p, %p)\n", iface, This, pCurrent, pStop);
854
855     FIXME("stub\n");
856     return E_NOTIMPL;
857 }
858
859 static HRESULT WINAPI MediaSeekingPassThru_GetAvailable(IMediaSeeking * iface, LONGLONG * pEarliest, LONGLONG * pLatest)
860 {
861     ICOM_THIS_MULTI(PassThruImpl, IMediaSeeking_vtbl, iface);
862
863     TRACE("(%p/%p)->(%p,%p)\n", iface, This, pEarliest, pLatest);
864
865     FIXME("stub\n");
866     return E_NOTIMPL;
867 }
868
869 static HRESULT WINAPI MediaSeekingPassThru_SetRate(IMediaSeeking * iface, double dRate)
870 {
871     ICOM_THIS_MULTI(PassThruImpl, IMediaSeeking_vtbl, iface);
872
873     TRACE("(%p/%p)->(%e)\n", iface, This, dRate);
874
875     FIXME("stub\n");
876     return E_NOTIMPL;
877 }
878
879 static HRESULT WINAPI MediaSeekingPassThru_GetRate(IMediaSeeking * iface, double * dRate)
880 {
881     ICOM_THIS_MULTI(PassThruImpl, IMediaSeeking_vtbl, iface);
882
883     TRACE("(%p/%p)->(%p)\n", iface, This, dRate);
884
885     FIXME("stub\n");
886     return E_NOTIMPL;
887 }
888
889 static HRESULT WINAPI MediaSeekingPassThru_GetPreroll(IMediaSeeking * iface, LONGLONG * pPreroll)
890 {
891     TRACE("(%p)\n", pPreroll);
892
893     FIXME("stub\n");
894     return E_NOTIMPL;
895 }
896
897 static const IMediaSeekingVtbl IMediaSeekingPassThru_Vtbl =
898 {
899     MediaSeekingPassThru_QueryInterface,
900     MediaSeekingPassThru_AddRef,
901     MediaSeekingPassThru_Release,
902     MediaSeekingPassThru_GetCapabilities,
903     MediaSeekingPassThru_CheckCapabilities,
904     MediaSeekingPassThru_IsFormatSupported,
905     MediaSeekingPassThru_QueryPreferredFormat,
906     MediaSeekingPassThru_GetTimeFormat,
907     MediaSeekingPassThru_IsUsingTimeFormat,
908     MediaSeekingPassThru_SetTimeFormat,
909     MediaSeekingPassThru_GetDuration,
910     MediaSeekingPassThru_GetStopPosition,
911     MediaSeekingPassThru_GetCurrentPosition,
912     MediaSeekingPassThru_ConvertTimeFormat,
913     MediaSeekingPassThru_SetPositions,
914     MediaSeekingPassThru_GetPositions,
915     MediaSeekingPassThru_GetAvailable,
916     MediaSeekingPassThru_SetRate,
917     MediaSeekingPassThru_GetRate,
918     MediaSeekingPassThru_GetPreroll
919 };