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