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