quartz: Add a stub for SeekingPassThru.
[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 typedef struct PassThruImpl {
33     const ISeekingPassThruVtbl *IPassThru_vtbl;
34     const IUnknownVtbl * IInner_vtbl;
35
36     LONG ref;
37     IUnknown * pUnkOuter;
38     BOOL bUnkOuterValid;
39     BOOL bAggregatable;
40 } PassThruImpl;
41
42 static HRESULT WINAPI SeekInner_QueryInterface(IUnknown * iface,
43                                           REFIID riid,
44                                           LPVOID *ppvObj) {
45     ICOM_THIS_MULTI(PassThruImpl, IInner_vtbl, iface);
46     TRACE("(%p)->(%s (%p), %p)\n", This, debugstr_guid(riid), riid, ppvObj);
47
48     if (This->bAggregatable)
49         This->bUnkOuterValid = TRUE;
50
51     if (IsEqualGUID(&IID_IUnknown, riid))
52     {
53         *ppvObj = &(This->IInner_vtbl);
54         TRACE("   returning IUnknown interface (%p)\n", *ppvObj);
55     } else if (IsEqualGUID(&IID_ISeekingPassThru, riid)) {
56         *ppvObj = &(This->IPassThru_vtbl);
57         TRACE("   returning IMediaSeeking interface (%p)\n", *ppvObj);
58     } else {
59         *ppvObj = NULL;
60         FIXME("unknown interface %s\n", debugstr_guid(riid));
61         return E_NOINTERFACE;
62     }
63
64     IUnknown_AddRef((IUnknown *)(*ppvObj));
65     return S_OK;
66 }
67
68 static ULONG WINAPI SeekInner_AddRef(IUnknown * iface) {
69     ICOM_THIS_MULTI(PassThruImpl, IInner_vtbl, iface);
70     ULONG ref = InterlockedIncrement(&This->ref);
71
72     TRACE("(%p)->(): new ref = %d\n", This, ref);
73
74     return ref;
75 }
76
77 static ULONG WINAPI SeekInner_Release(IUnknown * iface) {
78     ICOM_THIS_MULTI(PassThruImpl, IInner_vtbl, iface);
79     ULONG ref = InterlockedDecrement(&This->ref);
80
81     TRACE("(%p)->(): new ref = %d\n", This, ref);
82
83     if (ref == 0)
84     {
85         CoTaskMemFree(This);
86     }
87     return ref;
88 }
89
90 static const IUnknownVtbl IInner_VTable =
91 {
92     SeekInner_QueryInterface,
93     SeekInner_AddRef,
94     SeekInner_Release
95 };
96
97 /* Generic functions for aggegration */
98 static HRESULT WINAPI SeekOuter_QueryInterface(PassThruImpl *This, REFIID riid, LPVOID *ppv)
99 {
100     if (This->bAggregatable)
101         This->bUnkOuterValid = TRUE;
102
103     if (This->pUnkOuter)
104     {
105         if (This->bAggregatable)
106             return IUnknown_QueryInterface(This->pUnkOuter, riid, ppv);
107
108         if (IsEqualIID(riid, &IID_IUnknown))
109         {
110             HRESULT hr;
111
112             IUnknown_AddRef((IUnknown *)&(This->IInner_vtbl));
113             hr = IUnknown_QueryInterface((IUnknown *)&(This->IInner_vtbl), riid, ppv);
114             IUnknown_Release((IUnknown *)&(This->IInner_vtbl));
115             This->bAggregatable = TRUE;
116             return hr;
117         }
118
119         *ppv = NULL;
120         return E_NOINTERFACE;
121     }
122
123     return IUnknown_QueryInterface((IUnknown *)&(This->IInner_vtbl), riid, ppv);
124 }
125
126 static ULONG WINAPI SeekOuter_AddRef(PassThruImpl *This)
127 {
128     if (This->pUnkOuter && This->bUnkOuterValid)
129         return IUnknown_AddRef(This->pUnkOuter);
130     return IUnknown_AddRef((IUnknown *)&(This->IInner_vtbl));
131 }
132
133 static ULONG WINAPI SeekOuter_Release(PassThruImpl *This)
134 {
135     if (This->pUnkOuter && This->bUnkOuterValid)
136         return IUnknown_Release(This->pUnkOuter);
137     return IUnknown_Release((IUnknown *)&(This->IInner_vtbl));
138 }
139
140 static HRESULT WINAPI SeekingPassThru_QueryInterface(ISeekingPassThru *iface, REFIID riid, LPVOID *ppvObj)
141 {
142     ICOM_THIS_MULTI(PassThruImpl, IPassThru_vtbl, iface);
143
144     TRACE("(%p/%p)->(%s (%p), %p)\n", This, iface, debugstr_guid(riid), riid, ppvObj);
145
146     return SeekOuter_QueryInterface(This, riid, ppvObj);
147 }
148
149 static ULONG WINAPI SeekingPassThru_AddRef(ISeekingPassThru *iface)
150 {
151     ICOM_THIS_MULTI(PassThruImpl, IPassThru_vtbl, iface);
152
153     TRACE("(%p/%p)->()\n", This, iface);
154
155     return SeekOuter_AddRef(This);
156 }
157
158 static ULONG WINAPI SeekingPassThru_Release(ISeekingPassThru *iface)
159 {
160     ICOM_THIS_MULTI(PassThruImpl, IPassThru_vtbl, iface);
161
162     TRACE("(%p/%p)->()\n", This, iface);
163
164     return SeekOuter_Release(This);
165 }
166
167 static HRESULT WINAPI SeekingPassThru_Init(ISeekingPassThru *iface, BOOL renderer, IPin *pin)
168 {
169     ICOM_THIS_MULTI(PassThruImpl, IPassThru_vtbl, iface);
170
171     FIXME("(%p/%p)->(%d, %p) stub\n", This, iface, renderer, pin);
172
173     return S_OK;
174 }
175
176 static const ISeekingPassThruVtbl ISeekingPassThru_Vtbl =
177 {
178     SeekingPassThru_QueryInterface,
179     SeekingPassThru_AddRef,
180     SeekingPassThru_Release,
181     SeekingPassThru_Init
182 };
183
184 HRESULT SeekingPassThru_create(IUnknown *pUnkOuter, LPVOID *ppObj)
185 {
186     PassThruImpl *fimpl;
187
188     TRACE("(%p,%p)\n", pUnkOuter, ppObj);
189
190     *ppObj = fimpl = CoTaskMemAlloc(sizeof(*fimpl));
191     if (!fimpl)
192         return E_OUTOFMEMORY;
193
194     fimpl->pUnkOuter = pUnkOuter;
195     fimpl->bUnkOuterValid = FALSE;
196     fimpl->bAggregatable = FALSE;
197     fimpl->IInner_vtbl = &IInner_VTable;
198     fimpl->IPassThru_vtbl = &ISeekingPassThru_Vtbl;
199     fimpl->ref = 1;
200     return S_OK;
201 }
202
203 typedef HRESULT (*SeekFunc)( IMediaSeeking *to, LPVOID arg );
204
205 static HRESULT ForwardCmdSeek( PCRITICAL_SECTION crit_sect, IBaseFilter* from, SeekFunc fnSeek, LPVOID arg )
206 {
207     HRESULT hr = S_OK;
208     HRESULT hr_return = S_OK;
209     IEnumPins *enumpins = NULL;
210     BOOL foundend = FALSE, allnotimpl = TRUE;
211
212     hr = IBaseFilter_EnumPins( from, &enumpins );
213     if (FAILED(hr))
214         goto out;
215
216     hr = IEnumPins_Reset( enumpins );
217     while (hr == S_OK) {
218         IPin *pin = NULL;
219         hr = IEnumPins_Next( enumpins, 1, &pin, NULL );
220         if (hr == VFW_E_ENUM_OUT_OF_SYNC)
221         {
222             hr = IEnumPins_Reset( enumpins );
223             continue;
224         }
225         if (pin)
226         {
227             PIN_DIRECTION dir;
228
229             IPin_QueryDirection( pin, &dir );
230             if (dir == PINDIR_INPUT)
231             {
232                 IPin *connected = NULL;
233
234                 IPin_ConnectedTo( pin, &connected );
235                 if (connected)
236                 {
237                     HRESULT hr_local;
238                     IMediaSeeking *seek = NULL;
239
240                     hr_local = IPin_QueryInterface( connected, &IID_IMediaSeeking, (void**)&seek );
241                     if (!hr_local)
242                     {
243                         foundend = TRUE;
244                         LeaveCriticalSection( crit_sect );
245                         hr_local = fnSeek( seek , arg );
246                         EnterCriticalSection( crit_sect );
247                         if (hr_local != E_NOTIMPL)
248                             allnotimpl = FALSE;
249
250                         hr_return = updatehres( hr_return, hr_local );
251                         IMediaSeeking_Release( seek );
252                     }
253                     IPin_Release(connected);
254                 }
255             }
256             IPin_Release( pin );
257         }
258     }
259     if (foundend && allnotimpl)
260         hr = E_NOTIMPL;
261     else
262         hr = hr_return;
263
264 out:
265     TRACE("Returning: %08x\n", hr);
266     return hr;
267 }
268
269
270 HRESULT MediaSeekingImpl_Init(IBaseFilter *pUserData, CHANGEPROC fnChangeStop, CHANGEPROC fnChangeCurrent, CHANGEPROC fnChangeRate, MediaSeekingImpl * pSeeking, PCRITICAL_SECTION crit_sect)
271 {
272     assert(fnChangeStop && fnChangeCurrent && fnChangeRate);
273
274     pSeeking->refCount = 1;
275     pSeeking->pUserData = pUserData;
276     pSeeking->fnChangeRate = fnChangeRate;
277     pSeeking->fnChangeStop = fnChangeStop;
278     pSeeking->fnChangeCurrent = fnChangeCurrent;
279     pSeeking->dwCapabilities = AM_SEEKING_CanSeekForwards |
280         AM_SEEKING_CanSeekBackwards |
281         AM_SEEKING_CanSeekAbsolute |
282         AM_SEEKING_CanGetStopPos |
283         AM_SEEKING_CanGetDuration;
284     pSeeking->llCurrent = 0;
285     pSeeking->llStop = ((ULONGLONG)0x80000000) << 32;
286     pSeeking->llDuration = pSeeking->llStop;
287     pSeeking->dRate = 1.0;
288     pSeeking->timeformat = TIME_FORMAT_MEDIA_TIME;
289     pSeeking->crst = crit_sect;
290
291     return S_OK;
292 }
293
294
295 HRESULT WINAPI MediaSeekingImpl_GetCapabilities(IMediaSeeking * iface, DWORD * pCapabilities)
296 {
297     MediaSeekingImpl *This = (MediaSeekingImpl *)iface;
298
299     TRACE("(%p)\n", pCapabilities);
300
301     *pCapabilities = This->dwCapabilities;
302
303     return S_OK;
304 }
305
306 static HRESULT fwd_checkcaps(IMediaSeeking *iface, LPVOID pcaps)
307 {
308     DWORD *caps = pcaps;
309     return IMediaSeeking_CheckCapabilities(iface, caps);
310 }
311
312 HRESULT WINAPI MediaSeekingImpl_CheckCapabilities(IMediaSeeking * iface, DWORD * pCapabilities)
313 {
314     MediaSeekingImpl *This = (MediaSeekingImpl *)iface;
315     HRESULT hr;
316     DWORD dwCommonCaps;
317
318     TRACE("(%p)\n", pCapabilities);
319
320     if (!pCapabilities)
321         return E_POINTER;
322
323     EnterCriticalSection(This->crst);
324     hr = ForwardCmdSeek(This->crst, This->pUserData, fwd_checkcaps, pCapabilities);
325     LeaveCriticalSection(This->crst);
326     if (FAILED(hr) && hr != E_NOTIMPL)
327         return hr;
328
329     dwCommonCaps = *pCapabilities & This->dwCapabilities;
330
331     if (!dwCommonCaps)
332         hr = E_FAIL;
333     else
334         hr = (*pCapabilities == dwCommonCaps) ?  S_OK : S_FALSE;
335     *pCapabilities = dwCommonCaps;
336
337     return hr;
338 }
339
340 HRESULT WINAPI MediaSeekingImpl_IsFormatSupported(IMediaSeeking * iface, const GUID * pFormat)
341 {
342     TRACE("(%s)\n", qzdebugstr_guid(pFormat));
343
344     return (IsEqualIID(pFormat, &TIME_FORMAT_MEDIA_TIME) ? S_OK : S_FALSE);
345 }
346
347 HRESULT WINAPI MediaSeekingImpl_QueryPreferredFormat(IMediaSeeking * iface, GUID * pFormat)
348 {
349     TRACE("(%s)\n", qzdebugstr_guid(pFormat));
350
351     *pFormat = TIME_FORMAT_MEDIA_TIME;
352     return S_OK;
353 }
354
355 HRESULT WINAPI MediaSeekingImpl_GetTimeFormat(IMediaSeeking * iface, GUID * pFormat)
356 {
357     MediaSeekingImpl *This = (MediaSeekingImpl *)iface;
358     TRACE("(%s)\n", qzdebugstr_guid(pFormat));
359
360     EnterCriticalSection(This->crst);
361     *pFormat = This->timeformat;
362     LeaveCriticalSection(This->crst);
363
364     return S_OK;
365 }
366
367 HRESULT WINAPI MediaSeekingImpl_IsUsingTimeFormat(IMediaSeeking * iface, const GUID * pFormat)
368 {
369     MediaSeekingImpl *This = (MediaSeekingImpl *)iface;
370     HRESULT hr = S_OK;
371
372     TRACE("(%s)\n", qzdebugstr_guid(pFormat));
373
374     EnterCriticalSection(This->crst);
375     if (!IsEqualIID(pFormat, &This->timeformat))
376         hr = S_FALSE;
377     LeaveCriticalSection(This->crst);
378
379     return hr;
380 }
381
382
383 static HRESULT fwd_settimeformat(IMediaSeeking *iface, LPVOID pformat)
384 {
385     const GUID *format = pformat;
386     return IMediaSeeking_SetTimeFormat(iface, format);
387 }
388
389 HRESULT WINAPI MediaSeekingImpl_SetTimeFormat(IMediaSeeking * iface, const GUID * pFormat)
390 {
391     MediaSeekingImpl *This = (MediaSeekingImpl *)iface;
392     TRACE("(%s)\n", qzdebugstr_guid(pFormat));
393
394     EnterCriticalSection(This->crst);
395     ForwardCmdSeek(This->crst, This->pUserData, fwd_settimeformat, (LPVOID)pFormat);
396     LeaveCriticalSection(This->crst);
397
398     return (IsEqualIID(pFormat, &TIME_FORMAT_MEDIA_TIME) ? S_OK : S_FALSE);
399 }
400
401
402 static HRESULT fwd_getduration(IMediaSeeking *iface, LPVOID pdur)
403 {
404     LONGLONG *duration = pdur;
405     LONGLONG mydur = *duration;
406     HRESULT hr;
407
408     hr = IMediaSeeking_GetDuration(iface, &mydur);
409     if (FAILED(hr))
410         return hr;
411
412     if ((mydur < *duration) || (*duration < 0 && mydur > 0))
413         *duration = mydur;
414     return hr;
415 }
416
417 HRESULT WINAPI MediaSeekingImpl_GetDuration(IMediaSeeking * iface, LONGLONG * pDuration)
418 {
419     MediaSeekingImpl *This = (MediaSeekingImpl *)iface;
420
421     TRACE("(%p)\n", pDuration);
422
423     EnterCriticalSection(This->crst);
424     *pDuration = This->llDuration;
425     ForwardCmdSeek(This->crst, This->pUserData, fwd_getduration, pDuration);
426     LeaveCriticalSection(This->crst);
427
428     return S_OK;
429 }
430
431
432 static HRESULT fwd_getstopposition(IMediaSeeking *iface, LPVOID pdur)
433 {
434     LONGLONG *duration = pdur;
435     LONGLONG mydur = *duration;
436     HRESULT hr;
437
438     hr = IMediaSeeking_GetStopPosition(iface, &mydur);
439     if (FAILED(hr))
440         return hr;
441
442     if ((mydur < *duration) || (*duration < 0 && mydur > 0))
443         *duration = mydur;
444     return hr;
445 }
446
447 HRESULT WINAPI MediaSeekingImpl_GetStopPosition(IMediaSeeking * iface, LONGLONG * pStop)
448 {
449     MediaSeekingImpl *This = (MediaSeekingImpl *)iface;
450
451     TRACE("(%p)\n", pStop);
452
453     EnterCriticalSection(This->crst);
454     *pStop = This->llStop;
455     ForwardCmdSeek(This->crst, This->pUserData, fwd_getstopposition, pStop);
456     LeaveCriticalSection(This->crst);
457
458     return S_OK;
459 }
460
461
462 static HRESULT fwd_getcurposition(IMediaSeeking *iface, LPVOID pdur)
463 {
464     LONGLONG *duration = pdur;
465     LONGLONG mydur = *duration;
466     HRESULT hr;
467
468     hr = IMediaSeeking_GetCurrentPosition(iface, &mydur);
469     if (FAILED(hr))
470         return hr;
471
472     if ((mydur < *duration) || (*duration < 0 && mydur > 0))
473         *duration = mydur;
474     return hr;
475 }
476
477 /* FIXME: Make use of the info the filter should expose */
478 HRESULT WINAPI MediaSeekingImpl_GetCurrentPosition(IMediaSeeking * iface, LONGLONG * pCurrent)
479 {
480     MediaSeekingImpl *This = (MediaSeekingImpl *)iface;
481
482     TRACE("(%p)\n", pCurrent);
483
484     EnterCriticalSection(This->crst);
485     *pCurrent = This->llCurrent;
486     ForwardCmdSeek(This->crst, This->pUserData, fwd_getcurposition, pCurrent);
487     LeaveCriticalSection(This->crst);
488
489     return S_OK;
490 }
491
492 HRESULT WINAPI MediaSeekingImpl_ConvertTimeFormat(IMediaSeeking * iface, LONGLONG * pTarget, const GUID * pTargetFormat, LONGLONG Source, const GUID * pSourceFormat)
493 {
494     if (IsEqualIID(pTargetFormat, &TIME_FORMAT_MEDIA_TIME) && IsEqualIID(pSourceFormat, &TIME_FORMAT_MEDIA_TIME))
495     {
496         *pTarget = Source;
497         return S_OK;
498     }
499     /* FIXME: clear pTarget? */
500     return E_INVALIDARG;
501 }
502
503 static inline LONGLONG Adjust(LONGLONG value, const LONGLONG * pModifier, DWORD dwFlags)
504 {
505     switch (dwFlags & AM_SEEKING_PositioningBitsMask)
506     {
507     case AM_SEEKING_NoPositioning:
508         return value;
509     case AM_SEEKING_AbsolutePositioning:
510         return *pModifier;
511     case AM_SEEKING_RelativePositioning:
512     case AM_SEEKING_IncrementalPositioning:
513         return value + *pModifier;
514     default:
515         assert(FALSE);
516         return 0;
517     }
518 }
519
520 struct pos_args {
521     LONGLONG* current, *stop;
522     DWORD curflags, stopflags;
523 };
524
525 static HRESULT fwd_setposition(IMediaSeeking *seek, LPVOID pargs)
526 {
527     struct pos_args *args = (void*)pargs;
528
529     return IMediaSeeking_SetPositions(seek, args->current, args->curflags, args->stop, args->stopflags);
530 }
531
532
533 HRESULT WINAPI MediaSeekingImpl_SetPositions(IMediaSeeking * iface, LONGLONG * pCurrent, DWORD dwCurrentFlags, LONGLONG * pStop, DWORD dwStopFlags)
534 {
535     MediaSeekingImpl *This = (MediaSeekingImpl *)iface;
536     BOOL bChangeCurrent = FALSE, bChangeStop = FALSE;
537     LONGLONG llNewCurrent, llNewStop;
538     struct pos_args args;
539
540     TRACE("(%p, %x, %p, %x)\n", pCurrent, dwCurrentFlags, pStop, dwStopFlags);
541
542     args.current = pCurrent;
543     args.stop = pStop;
544     args.curflags = dwCurrentFlags;
545     args.stopflags = dwStopFlags;
546
547     EnterCriticalSection(This->crst);
548
549     llNewCurrent = Adjust(This->llCurrent, pCurrent, dwCurrentFlags);
550     llNewStop = Adjust(This->llStop, pStop, dwStopFlags);
551
552     if (llNewCurrent != This->llCurrent)
553         bChangeCurrent = TRUE;
554     if (llNewStop != This->llStop)
555         bChangeStop = TRUE;
556
557     TRACE("Old: %u, New: %u\n", (DWORD)(This->llCurrent/10000000), (DWORD)(llNewCurrent/10000000));
558
559     This->llCurrent = llNewCurrent;
560     This->llStop = llNewStop;
561
562     if (dwCurrentFlags & AM_SEEKING_ReturnTime)
563         *pCurrent = llNewCurrent;
564     if (dwStopFlags & AM_SEEKING_ReturnTime)
565         *pStop = llNewStop;
566
567     ForwardCmdSeek(This->crst, This->pUserData, fwd_setposition, &args);
568     LeaveCriticalSection(This->crst);
569
570     if (bChangeCurrent)
571         This->fnChangeCurrent(This->pUserData);
572     if (bChangeStop)
573         This->fnChangeStop(This->pUserData);
574
575     return S_OK;
576 }
577
578 HRESULT WINAPI MediaSeekingImpl_GetPositions(IMediaSeeking * iface, LONGLONG * pCurrent, LONGLONG * pStop)
579 {
580     MediaSeekingImpl *This = (MediaSeekingImpl *)iface;
581
582     TRACE("(%p, %p)\n", pCurrent, pStop);
583
584     EnterCriticalSection(This->crst);
585     *pCurrent = This->llCurrent;
586     *pStop = This->llStop;
587     LeaveCriticalSection(This->crst);
588
589     return S_OK;
590 }
591
592 HRESULT WINAPI MediaSeekingImpl_GetAvailable(IMediaSeeking * iface, LONGLONG * pEarliest, LONGLONG * pLatest)
593 {
594     MediaSeekingImpl *This = (MediaSeekingImpl *)iface;
595
596     TRACE("(%p, %p)\n", pEarliest, pLatest);
597
598     EnterCriticalSection(This->crst);
599     *pEarliest = 0;
600     *pLatest = This->llDuration;
601     LeaveCriticalSection(This->crst);
602
603     return S_OK;
604 }
605
606 static HRESULT fwd_setrate(IMediaSeeking *iface, LPVOID prate)
607 {
608     double *rate = prate;
609
610     HRESULT hr;
611
612     hr = IMediaSeeking_SetRate(iface, *rate);
613     if (FAILED(hr))
614         return hr;
615
616     return hr;
617 }
618
619 HRESULT WINAPI MediaSeekingImpl_SetRate(IMediaSeeking * iface, double dRate)
620 {
621     MediaSeekingImpl *This = (MediaSeekingImpl *)iface;
622     BOOL bChangeRate = (dRate != This->dRate);
623     HRESULT hr = S_OK;
624
625     TRACE("(%e)\n", dRate);
626
627     if (dRate > 100 || dRate < .001)
628     {
629         FIXME("Excessive rate %e, ignoring\n", dRate);
630         return VFW_E_UNSUPPORTED_AUDIO;
631     }
632
633     EnterCriticalSection(This->crst);
634     This->dRate = dRate;
635     if (bChangeRate)
636         hr = This->fnChangeRate(This->pUserData);
637     ForwardCmdSeek(This->crst, This->pUserData, fwd_setrate, &dRate);
638     LeaveCriticalSection(This->crst);
639
640     return hr;
641 }
642
643 HRESULT WINAPI MediaSeekingImpl_GetRate(IMediaSeeking * iface, double * dRate)
644 {
645     MediaSeekingImpl *This = (MediaSeekingImpl *)iface;
646
647     TRACE("(%p)\n", dRate);
648
649     EnterCriticalSection(This->crst);
650     /* Forward? */
651     *dRate = This->dRate;
652     LeaveCriticalSection(This->crst);
653
654     return S_OK;
655 }
656
657 HRESULT WINAPI MediaSeekingImpl_GetPreroll(IMediaSeeking * iface, LONGLONG * pPreroll)
658 {
659     TRACE("(%p)\n", pPreroll);
660
661     *pPreroll = 0;
662     return S_OK;
663 }