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