strmbase: Implement renderer quality control into the base renderer.
[wine] / dlls / strmbase / renderer.c
1 /*
2  * Generic Implementation of strmbase Base Renderer classes
3  *
4  * Copyright 2012 Aric Stewart, CodeWeavers
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 #define COBJMACROS
22
23 #include "dshow.h"
24 #include "wine/debug.h"
25 #include "wine/unicode.h"
26 #include "wine/strmbase.h"
27 #include "uuids.h"
28 #include "vfwmsgs.h"
29
30 WINE_DEFAULT_DEBUG_CHANNEL(strmbase);
31
32 static const WCHAR wcsInputPinName[] = {'i','n','p','u','t',' ','p','i','n',0};
33 static const WCHAR wcsAltInputPinName[] = {'I','n',0};
34
35 static inline BaseInputPin *impl_BaseInputPin_from_IPin( IPin *iface )
36 {
37     return CONTAINING_RECORD(iface, BaseInputPin, pin.IPin_iface);
38 }
39
40 static inline BaseRenderer *impl_from_IBaseFilter(IBaseFilter *iface)
41 {
42     return CONTAINING_RECORD(iface, BaseRenderer, filter.IBaseFilter_iface);
43 }
44
45 static inline BaseRenderer *impl_from_BaseFilter(BaseFilter *iface)
46 {
47     return CONTAINING_RECORD(iface, BaseRenderer, filter);
48 }
49
50 static const IQualityControlVtbl Renderer_QualityControl_Vtbl = {
51     QualityControlImpl_QueryInterface,
52     QualityControlImpl_AddRef,
53     QualityControlImpl_Release,
54     QualityControlImpl_Notify,
55     QualityControlImpl_SetSink
56 };
57
58 static HRESULT WINAPI BaseRenderer_InputPin_ReceiveConnection(IPin * iface, IPin * pReceivePin, const AM_MEDIA_TYPE * pmt)
59 {
60     BaseInputPin *This = impl_BaseInputPin_from_IPin(iface);
61     BaseRenderer *renderer = impl_from_IBaseFilter(This->pin.pinInfo.pFilter);
62     HRESULT hr = S_OK;
63
64     TRACE("(%p/%p)->(%p, %p)\n", This, renderer, pReceivePin, pmt);
65
66     EnterCriticalSection(This->pin.pCritSec);
67     hr = BaseInputPinImpl_ReceiveConnection(iface, pReceivePin, pmt);
68     if (SUCCEEDED(hr))
69     {
70         if (renderer->pFuncsTable->pfnCompleteConnect)
71             hr = renderer->pFuncsTable->pfnCompleteConnect(renderer, pReceivePin);
72     }
73     LeaveCriticalSection(This->pin.pCritSec);
74
75     return hr;
76 }
77
78 static HRESULT WINAPI BaseRenderer_InputPin_Disconnect(IPin * iface)
79 {
80     BaseInputPin *This = impl_BaseInputPin_from_IPin(iface);
81     BaseRenderer *renderer = impl_from_IBaseFilter(This->pin.pinInfo.pFilter);
82     HRESULT hr;
83
84     TRACE("(%p/%p)\n", This, renderer);
85
86     EnterCriticalSection(This->pin.pCritSec);
87     hr = BasePinImpl_Disconnect(iface);
88     if (SUCCEEDED(hr))
89     {
90         if (renderer->pFuncsTable->pfnBreakConnect)
91             hr = renderer->pFuncsTable->pfnBreakConnect(renderer);
92     }
93     BaseRendererImpl_ClearPendingSample(renderer);
94     LeaveCriticalSection(This->pin.pCritSec);
95
96     return hr;
97 }
98
99 static HRESULT WINAPI BaseRenderer_InputPin_EndOfStream(IPin * iface)
100 {
101     HRESULT hr = S_OK;
102     BaseInputPin* This = impl_BaseInputPin_from_IPin(iface);
103     BaseRenderer *pFilter = impl_from_IBaseFilter(This->pin.pinInfo.pFilter);
104
105     TRACE("(%p/%p)->()\n", This, pFilter);
106
107     EnterCriticalSection(&pFilter->filter.csFilter);
108     EnterCriticalSection(&pFilter->csRenderLock);
109     hr = BaseInputPinImpl_EndOfStream(iface);
110     EnterCriticalSection(This->pin.pCritSec);
111     if (SUCCEEDED(hr))
112     {
113         if (pFilter->pFuncsTable->pfnEndOfStream)
114             hr = pFilter->pFuncsTable->pfnEndOfStream(pFilter);
115         else
116             hr = BaseRendererImpl_EndOfStream(pFilter);
117     }
118     LeaveCriticalSection(This->pin.pCritSec);
119     LeaveCriticalSection(&pFilter->csRenderLock);
120     LeaveCriticalSection(&pFilter->filter.csFilter);
121     return hr;
122 }
123
124 static HRESULT WINAPI BaseRenderer_InputPin_BeginFlush(IPin * iface)
125 {
126     BaseInputPin* This = impl_BaseInputPin_from_IPin(iface);
127     BaseRenderer *pFilter = impl_from_IBaseFilter(This->pin.pinInfo.pFilter);
128     HRESULT hr = S_OK;
129
130     TRACE("(%p/%p)->()\n", This, iface);
131
132     EnterCriticalSection(&pFilter->filter.csFilter);
133     EnterCriticalSection(&pFilter->csRenderLock);
134     EnterCriticalSection(This->pin.pCritSec);
135     hr = BaseInputPinImpl_BeginFlush(iface);
136     if (SUCCEEDED(hr))
137     {
138         if (pFilter->pFuncsTable->pfnBeginFlush)
139             hr = pFilter->pFuncsTable->pfnBeginFlush(pFilter);
140         else
141             hr = BaseRendererImpl_BeginFlush(pFilter);
142     }
143     LeaveCriticalSection(This->pin.pCritSec);
144     LeaveCriticalSection(&pFilter->filter.csFilter);
145     LeaveCriticalSection(&pFilter->csRenderLock);
146     return hr;
147 }
148
149 static HRESULT WINAPI BaseRenderer_InputPin_EndFlush(IPin * iface)
150 {
151     BaseInputPin* This = impl_BaseInputPin_from_IPin(iface);
152     BaseRenderer *pFilter = impl_from_IBaseFilter(This->pin.pinInfo.pFilter);
153     HRESULT hr = S_OK;
154
155     TRACE("(%p/%p)->()\n", This, pFilter);
156
157     EnterCriticalSection(&pFilter->filter.csFilter);
158     EnterCriticalSection(&pFilter->csRenderLock);
159     EnterCriticalSection(This->pin.pCritSec);
160     hr = BaseInputPinImpl_EndFlush(iface);
161     if (SUCCEEDED(hr))
162     {
163         if (pFilter->pFuncsTable->pfnEndFlush)
164             hr = pFilter->pFuncsTable->pfnEndFlush(pFilter);
165         else
166             hr = BaseRendererImpl_EndFlush(pFilter);
167     }
168     LeaveCriticalSection(This->pin.pCritSec);
169     LeaveCriticalSection(&pFilter->csRenderLock);
170     LeaveCriticalSection(&pFilter->filter.csFilter);
171     return hr;
172 }
173
174 static const IPinVtbl BaseRenderer_InputPin_Vtbl =
175 {
176     BaseInputPinImpl_QueryInterface,
177     BasePinImpl_AddRef,
178     BaseInputPinImpl_Release,
179     BaseInputPinImpl_Connect,
180     BaseRenderer_InputPin_ReceiveConnection,
181     BaseRenderer_InputPin_Disconnect,
182     BasePinImpl_ConnectedTo,
183     BasePinImpl_ConnectionMediaType,
184     BasePinImpl_QueryPinInfo,
185     BasePinImpl_QueryDirection,
186     BasePinImpl_QueryId,
187     BaseInputPinImpl_QueryAccept,
188     BasePinImpl_EnumMediaTypes,
189     BasePinImpl_QueryInternalConnections,
190     BaseRenderer_InputPin_EndOfStream,
191     BaseRenderer_InputPin_BeginFlush,
192     BaseRenderer_InputPin_EndFlush,
193     BaseInputPinImpl_NewSegment
194 };
195
196 static IPin* WINAPI BaseRenderer_GetPin(BaseFilter *iface, int pos)
197 {
198     BaseRenderer *This = impl_from_BaseFilter(iface);
199
200     if (pos >= 1 || pos < 0)
201         return NULL;
202
203     IPin_AddRef(&This->pInputPin->pin.IPin_iface);
204     return &This->pInputPin->pin.IPin_iface;
205 }
206
207 static LONG WINAPI BaseRenderer_GetPinCount(BaseFilter *iface)
208 {
209     return 1;
210 }
211
212 static HRESULT WINAPI BaseRenderer_Input_CheckMediaType(BasePin *pin, const AM_MEDIA_TYPE * pmt)
213 {
214     BaseRenderer *This = impl_from_IBaseFilter(pin->pinInfo.pFilter);
215     return This->pFuncsTable->pfnCheckMediaType(This, pmt);
216 }
217
218 static HRESULT WINAPI BaseRenderer_Receive(BaseInputPin *pin, IMediaSample * pSample)
219 {
220     BaseRenderer *This = impl_from_IBaseFilter(pin->pin.pinInfo.pFilter);
221     return BaseRendererImpl_Receive(This, pSample);
222 }
223
224 static const BaseFilterFuncTable RendererBaseFilterFuncTable = {
225     BaseRenderer_GetPin,
226     BaseRenderer_GetPinCount
227 };
228
229 static const  BasePinFuncTable input_BaseFuncTable = {
230     BaseRenderer_Input_CheckMediaType,
231     NULL,
232     BasePinImpl_GetMediaTypeVersion,
233     BasePinImpl_GetMediaType
234 };
235
236 static const BaseInputPinFuncTable input_BaseInputFuncTable = {
237    BaseRenderer_Receive
238 };
239
240
241 HRESULT WINAPI BaseRenderer_Init(BaseRenderer * This, const IBaseFilterVtbl *Vtbl, IUnknown *pUnkOuter, const CLSID *pClsid, DWORD_PTR DebugInfo, const BaseRendererFuncTable* pBaseFuncsTable)
242 {
243     PIN_INFO piInput;
244     HRESULT hr;
245
246     BaseFilter_Init(&This->filter, Vtbl, pClsid, DebugInfo, &RendererBaseFilterFuncTable);
247
248     This->pFuncsTable = pBaseFuncsTable;
249
250     /* construct input pin */
251     piInput.dir = PINDIR_INPUT;
252     piInput.pFilter = &This->filter.IBaseFilter_iface;
253     lstrcpynW(piInput.achName, wcsInputPinName, sizeof(piInput.achName) / sizeof(piInput.achName[0]));
254
255     hr = BaseInputPin_Construct(&BaseRenderer_InputPin_Vtbl, &piInput, &input_BaseFuncTable, &input_BaseInputFuncTable, &This->filter.csFilter, NULL, (IPin **)&This->pInputPin);
256
257     if (SUCCEEDED(hr))
258     {
259         hr = CreatePosPassThru(pUnkOuter ? pUnkOuter: (IUnknown*)This, TRUE, &This->pInputPin->pin.IPin_iface, &This->pPosition);
260         if (FAILED(hr))
261             return hr;
262
263         InitializeCriticalSection(&This->csRenderLock);
264         This->csRenderLock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__": BaseRenderer.csRenderLock");
265         This->evComplete = CreateEventW(NULL, TRUE, TRUE, NULL);
266         This->ThreadSignal = CreateEventW(NULL, TRUE, TRUE, NULL);
267         This->RenderEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
268         This->pMediaSample = NULL;
269
270         QualityControlImpl_init(&This->qcimpl, &This->pInputPin->pin.IPin_iface, &This->filter.IBaseFilter_iface);
271         This->qcimpl.lpVtbl = &Renderer_QualityControl_Vtbl;
272     }
273
274     return hr;
275 }
276
277 HRESULT WINAPI BaseRendererImpl_QueryInterface(IBaseFilter* iface, REFIID riid, LPVOID * ppv)
278 {
279     BaseRenderer *This = impl_from_IBaseFilter(iface);
280
281     if (IsEqualIID(riid, &IID_IMediaSeeking))
282         return IUnknown_QueryInterface(This->pPosition, riid, ppv);
283     else if (IsEqualIID(riid, &IID_IQualityControl))
284     {
285         *ppv = &This->qcimpl.lpVtbl;
286         IUnknown_AddRef((IUnknown *)(*ppv));
287         return S_OK;
288     }
289     else
290         return BaseFilterImpl_QueryInterface(iface, riid, ppv);
291 }
292
293 ULONG WINAPI BaseRendererImpl_Release(IBaseFilter* iface)
294 {
295     BaseRenderer *This = impl_from_IBaseFilter(iface);
296     ULONG refCount = BaseFilterImpl_Release(iface);
297
298     if (!refCount)
299     {
300         IPin *pConnectedTo;
301
302         if (SUCCEEDED(IPin_ConnectedTo(&This->pInputPin->pin.IPin_iface, &pConnectedTo)))
303         {
304             IPin_Disconnect(pConnectedTo);
305             IPin_Release(pConnectedTo);
306         }
307         IPin_Disconnect(&This->pInputPin->pin.IPin_iface);
308         IPin_Release(&This->pInputPin->pin.IPin_iface);
309
310         if (This->pPosition)
311             IUnknown_Release(This->pPosition);
312
313         This->csRenderLock.DebugInfo->Spare[0] = 0;
314         DeleteCriticalSection(&This->csRenderLock);
315
316         BaseRendererImpl_ClearPendingSample(This);
317         CloseHandle(This->evComplete);
318         CloseHandle(This->ThreadSignal);
319         CloseHandle(This->RenderEvent);
320     }
321     return refCount;
322 }
323
324 HRESULT WINAPI BaseRendererImpl_Receive(BaseRenderer *This, IMediaSample * pSample)
325 {
326     HRESULT hr = S_OK;
327     REFERENCE_TIME start, stop;
328     AM_MEDIA_TYPE *pmt;
329
330     TRACE("(%p)->%p\n", This, pSample);
331
332     if (This->pInputPin->end_of_stream || This->pInputPin->flushing)
333         return S_FALSE;
334
335     if (This->filter.state == State_Stopped)
336         return VFW_E_WRONG_STATE;
337
338     if (IMediaSample_GetMediaType(pSample, &pmt) == S_OK)
339     {
340         if (FAILED(This->pFuncsTable->pfnCheckMediaType(This, pmt)))
341         {
342             return VFW_E_TYPE_NOT_ACCEPTED;
343         }
344     }
345
346     This->pMediaSample = pSample;
347     IMediaSample_AddRef(pSample);
348
349     if (This->pFuncsTable->pfnPrepareReceive)
350         hr = This->pFuncsTable->pfnPrepareReceive(This, pSample);
351     if (FAILED(hr))
352     {
353         if (hr == VFW_E_SAMPLE_REJECTED)
354             return S_OK;
355         else
356             return hr;
357     }
358
359     if (This->pFuncsTable->pfnPrepareRender)
360         This->pFuncsTable->pfnPrepareRender(This);
361
362     EnterCriticalSection(&This->csRenderLock);
363     if ( This->filter.state == State_Paused )
364     {
365         if (This->pFuncsTable->pfnOnReceiveFirstSample)
366             This->pFuncsTable->pfnOnReceiveFirstSample(This, pSample);
367
368         SetEvent(This->evComplete);
369     }
370
371     /* Wait for render Time */
372     if (SUCCEEDED(IMediaSample_GetMediaTime(pSample, &start, &stop)))
373     {
374         hr = S_FALSE;
375         RendererPosPassThru_RegisterMediaTime(This->pPosition, start);
376         if (This->pFuncsTable->pfnShouldDrawSampleNow)
377             hr = This->pFuncsTable->pfnShouldDrawSampleNow(This, pSample, &start, &stop);
378
379         if (hr == S_OK)
380             ;/* Do not wait: drop through */
381         else if (hr == S_FALSE)
382         {
383             if (This->pFuncsTable->pfnOnWaitStart)
384                 This->pFuncsTable->pfnOnWaitStart(This);
385
386             hr = QualityControlRender_WaitFor(&This->qcimpl, pSample, This->RenderEvent);
387
388             if (This->pFuncsTable->pfnOnWaitEnd)
389                 This->pFuncsTable->pfnOnWaitEnd(This);
390         }
391         else
392         {
393             LeaveCriticalSection(&This->csRenderLock);
394             /* Drop Sample */
395             return S_OK;
396         }
397     }
398
399     if (SUCCEEDED(hr))
400     {
401         QualityControlRender_BeginRender(&This->qcimpl);
402         hr = This->pFuncsTable->pfnDoRenderSample(This, pSample);
403         QualityControlRender_EndRender(&This->qcimpl);
404     }
405
406     QualityControlRender_DoQOS(&This->qcimpl);
407
408     BaseRendererImpl_ClearPendingSample(This);
409     LeaveCriticalSection(&This->csRenderLock);
410
411     return hr;
412 }
413
414 HRESULT WINAPI BaseRendererImpl_FindPin(IBaseFilter * iface, LPCWSTR Id, IPin **ppPin)
415 {
416     BaseRenderer *This = impl_from_IBaseFilter(iface);
417
418     TRACE("(%p)->(%p,%p)\n", This, debugstr_w(Id), ppPin);
419
420     if (!Id || !ppPin)
421         return E_POINTER;
422
423     if (!lstrcmpiW(Id,wcsInputPinName) || !lstrcmpiW(Id,wcsAltInputPinName))
424     {
425         *ppPin = &This->pInputPin->pin.IPin_iface;
426         IPin_AddRef(*ppPin);
427         return S_OK;
428     }
429     *ppPin = NULL;
430     return VFW_E_NOT_FOUND;
431 }
432
433 HRESULT WINAPI BaseRendererImpl_Stop(IBaseFilter * iface)
434 {
435     BaseRenderer *This = impl_from_IBaseFilter(iface);
436
437     TRACE("(%p)->()\n", This);
438
439     EnterCriticalSection(&This->csRenderLock);
440     {
441         RendererPosPassThru_ResetMediaTime(This->pPosition);
442         if (This->pFuncsTable->pfnOnStopStreaming)
443             This->pFuncsTable->pfnOnStopStreaming(This);
444         This->filter.state = State_Stopped;
445         SetEvent(This->evComplete);
446         SetEvent(This->ThreadSignal);
447         SetEvent(This->RenderEvent);
448     }
449     LeaveCriticalSection(&This->csRenderLock);
450
451     return S_OK;
452 }
453
454 HRESULT WINAPI BaseRendererImpl_Run(IBaseFilter * iface, REFERENCE_TIME tStart)
455 {
456     HRESULT hr = S_OK;
457     BaseRenderer *This = impl_from_IBaseFilter(iface);
458     TRACE("(%p)->(%s)\n", This, wine_dbgstr_longlong(tStart));
459
460     EnterCriticalSection(&This->csRenderLock);
461     This->filter.rtStreamStart = tStart;
462     if (This->filter.state == State_Running)
463         goto out;
464
465     SetEvent(This->evComplete);
466     ResetEvent(This->ThreadSignal);
467
468     if (This->pInputPin->pin.pConnectedTo)
469     {
470         This->pInputPin->end_of_stream = 0;
471     }
472     else if (This->filter.filterInfo.pGraph)
473     {
474         IMediaEventSink *pEventSink;
475         hr = IFilterGraph_QueryInterface(This->filter.filterInfo.pGraph, &IID_IMediaEventSink, (LPVOID*)&pEventSink);
476         if (SUCCEEDED(hr))
477         {
478             hr = IMediaEventSink_Notify(pEventSink, EC_COMPLETE, S_OK, (LONG_PTR)This);
479             IMediaEventSink_Release(pEventSink);
480         }
481         hr = S_OK;
482     }
483     if (SUCCEEDED(hr))
484     {
485         QualityControlRender_Start(&This->qcimpl, This->filter.rtStreamStart);
486         if (This->pFuncsTable->pfnOnStartStreaming)
487             This->pFuncsTable->pfnOnStartStreaming(This);
488         if (This->filter.state == State_Stopped)
489             BaseRendererImpl_ClearPendingSample(This);
490         SetEvent(This->RenderEvent);
491         This->filter.state = State_Running;
492     }
493 out:
494     LeaveCriticalSection(&This->csRenderLock);
495
496     return hr;
497 }
498
499 HRESULT WINAPI BaseRendererImpl_Pause(IBaseFilter * iface)
500 {
501     BaseRenderer *This = impl_from_IBaseFilter(iface);
502
503     TRACE("(%p)->()\n", This);
504
505     EnterCriticalSection(&This->csRenderLock);
506     {
507      if (This->filter.state != State_Paused)
508         {
509             if (This->filter.state == State_Stopped)
510             {
511                 if (This->pInputPin->pin.pConnectedTo)
512                     ResetEvent(This->evComplete);
513                 This->pInputPin->end_of_stream = 0;
514             }
515             else if (This->pFuncsTable->pfnOnStopStreaming)
516                 This->pFuncsTable->pfnOnStopStreaming(This);
517
518             if (This->filter.state == State_Stopped)
519                 BaseRendererImpl_ClearPendingSample(This);
520             ResetEvent(This->RenderEvent);
521             This->filter.state = State_Paused;
522         }
523     }
524     ResetEvent(This->ThreadSignal);
525     LeaveCriticalSection(&This->csRenderLock);
526
527     return S_OK;
528 }
529
530 HRESULT WINAPI BaseRendererImpl_SetSyncSource(IBaseFilter *iface, IReferenceClock *clock)
531 {
532     BaseRenderer *This = impl_from_IBaseFilter(iface);
533     HRESULT hr;
534
535     EnterCriticalSection(&This->filter.csFilter);
536     QualityControlRender_SetClock(&This->qcimpl, clock);
537     hr = BaseFilterImpl_SetSyncSource(iface, clock);
538     LeaveCriticalSection(&This->filter.csFilter);
539     return hr;
540 }
541
542
543 HRESULT WINAPI BaseRendererImpl_GetState(IBaseFilter * iface, DWORD dwMilliSecsTimeout, FILTER_STATE *pState)
544 {
545     HRESULT hr;
546     BaseRenderer *This = impl_from_IBaseFilter(iface);
547
548     TRACE("(%p)->(%d, %p)\n", This, dwMilliSecsTimeout, pState);
549
550     if (WaitForSingleObject(This->evComplete, dwMilliSecsTimeout) == WAIT_TIMEOUT)
551         hr = VFW_S_STATE_INTERMEDIATE;
552     else
553         hr = S_OK;
554
555     BaseFilterImpl_GetState(iface, dwMilliSecsTimeout, pState);
556
557     return hr;
558 }
559
560 HRESULT WINAPI BaseRendererImpl_EndOfStream(BaseRenderer* iface)
561 {
562     IMediaEventSink* pEventSink;
563     IFilterGraph *graph;
564     HRESULT hr = S_OK;
565
566     TRACE("(%p)\n", iface);
567
568     graph = iface->filter.filterInfo.pGraph;
569     if (graph)
570     {        hr = IFilterGraph_QueryInterface(iface->filter.filterInfo.pGraph, &IID_IMediaEventSink, (LPVOID*)&pEventSink);
571         if (SUCCEEDED(hr))
572         {
573             hr = IMediaEventSink_Notify(pEventSink, EC_COMPLETE, S_OK, (LONG_PTR)iface);
574             IMediaEventSink_Release(pEventSink);
575         }
576     }
577     RendererPosPassThru_EOS(iface->pPosition);
578     SetEvent(iface->evComplete);
579
580     return hr;
581 }
582
583 HRESULT WINAPI BaseRendererImpl_BeginFlush(BaseRenderer* iface)
584 {
585     TRACE("(%p)\n", iface);
586     BaseRendererImpl_ClearPendingSample(iface);
587     SetEvent(iface->ThreadSignal);
588     SetEvent(iface->RenderEvent);
589     return S_OK;
590 }
591
592 HRESULT WINAPI BaseRendererImpl_EndFlush(BaseRenderer* iface)
593 {
594     TRACE("(%p)\n", iface);
595     QualityControlRender_Start(&iface->qcimpl, iface->filter.rtStreamStart);
596     RendererPosPassThru_ResetMediaTime(iface->pPosition);
597     ResetEvent(iface->ThreadSignal);
598     ResetEvent(iface->RenderEvent);
599     return S_OK;
600 }
601
602 HRESULT WINAPI BaseRendererImpl_ClearPendingSample(BaseRenderer *iface)
603 {
604     if (iface->pMediaSample)
605     {
606         IMediaSample_Release(iface->pMediaSample);
607         iface->pMediaSample = NULL;
608     }
609     return S_OK;
610 }