renderer.c 18 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
/*
 * Generic Implementation of strmbase Base Renderer classes
 *
 * Copyright 2012 Aric Stewart, CodeWeavers
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */

21
#include "strmbase_private.h"
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39

WINE_DEFAULT_DEBUG_CHANNEL(strmbase);

static inline BaseInputPin *impl_BaseInputPin_from_IPin( IPin *iface )
{
    return CONTAINING_RECORD(iface, BaseInputPin, pin.IPin_iface);
}

static inline BaseRenderer *impl_from_IBaseFilter(IBaseFilter *iface)
{
    return CONTAINING_RECORD(iface, BaseRenderer, filter.IBaseFilter_iface);
}

static inline BaseRenderer *impl_from_BaseFilter(BaseFilter *iface)
{
    return CONTAINING_RECORD(iface, BaseRenderer, filter);
}

40 41 42 43 44 45 46 47
static const IQualityControlVtbl Renderer_QualityControl_Vtbl = {
    QualityControlImpl_QueryInterface,
    QualityControlImpl_AddRef,
    QualityControlImpl_Release,
    QualityControlImpl_Notify,
    QualityControlImpl_SetSink
};

48 49 50 51
static HRESULT WINAPI BaseRenderer_InputPin_ReceiveConnection(IPin * iface, IPin * pReceivePin, const AM_MEDIA_TYPE * pmt)
{
    BaseInputPin *This = impl_BaseInputPin_from_IPin(iface);
    BaseRenderer *renderer = impl_from_IBaseFilter(This->pin.pinInfo.pFilter);
52
    HRESULT hr;
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82

    TRACE("(%p/%p)->(%p, %p)\n", This, renderer, pReceivePin, pmt);

    EnterCriticalSection(This->pin.pCritSec);
    hr = BaseInputPinImpl_ReceiveConnection(iface, pReceivePin, pmt);
    if (SUCCEEDED(hr))
    {
        if (renderer->pFuncsTable->pfnCompleteConnect)
            hr = renderer->pFuncsTable->pfnCompleteConnect(renderer, pReceivePin);
    }
    LeaveCriticalSection(This->pin.pCritSec);

    return hr;
}

static HRESULT WINAPI BaseRenderer_InputPin_Disconnect(IPin * iface)
{
    BaseInputPin *This = impl_BaseInputPin_from_IPin(iface);
    BaseRenderer *renderer = impl_from_IBaseFilter(This->pin.pinInfo.pFilter);
    HRESULT hr;

    TRACE("(%p/%p)\n", This, renderer);

    EnterCriticalSection(This->pin.pCritSec);
    hr = BasePinImpl_Disconnect(iface);
    if (SUCCEEDED(hr))
    {
        if (renderer->pFuncsTable->pfnBreakConnect)
            hr = renderer->pFuncsTable->pfnBreakConnect(renderer);
    }
83
    BaseRendererImpl_ClearPendingSample(renderer);
84 85 86 87 88
    LeaveCriticalSection(This->pin.pCritSec);

    return hr;
}

89 90
static HRESULT WINAPI BaseRenderer_InputPin_EndOfStream(IPin * iface)
{
91
    HRESULT hr;
92 93 94 95 96 97
    BaseInputPin* This = impl_BaseInputPin_from_IPin(iface);
    BaseRenderer *pFilter = impl_from_IBaseFilter(This->pin.pinInfo.pFilter);

    TRACE("(%p/%p)->()\n", This, pFilter);

    EnterCriticalSection(&pFilter->csRenderLock);
98
    EnterCriticalSection(&pFilter->filter.csFilter);
99 100 101 102 103 104 105 106 107 108 109
    hr = BaseInputPinImpl_EndOfStream(iface);
    EnterCriticalSection(This->pin.pCritSec);
    if (SUCCEEDED(hr))
    {
        if (pFilter->pFuncsTable->pfnEndOfStream)
            hr = pFilter->pFuncsTable->pfnEndOfStream(pFilter);
        else
            hr = BaseRendererImpl_EndOfStream(pFilter);
    }
    LeaveCriticalSection(This->pin.pCritSec);
    LeaveCriticalSection(&pFilter->filter.csFilter);
110
    LeaveCriticalSection(&pFilter->csRenderLock);
111 112 113
    return hr;
}

114 115 116 117
static HRESULT WINAPI BaseRenderer_InputPin_BeginFlush(IPin * iface)
{
    BaseInputPin* This = impl_BaseInputPin_from_IPin(iface);
    BaseRenderer *pFilter = impl_from_IBaseFilter(This->pin.pinInfo.pFilter);
118
    HRESULT hr;
119 120 121 122

    TRACE("(%p/%p)->()\n", This, iface);

    EnterCriticalSection(&pFilter->csRenderLock);
123
    EnterCriticalSection(&pFilter->filter.csFilter);
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
    EnterCriticalSection(This->pin.pCritSec);
    hr = BaseInputPinImpl_BeginFlush(iface);
    if (SUCCEEDED(hr))
    {
        if (pFilter->pFuncsTable->pfnBeginFlush)
            hr = pFilter->pFuncsTable->pfnBeginFlush(pFilter);
        else
            hr = BaseRendererImpl_BeginFlush(pFilter);
    }
    LeaveCriticalSection(This->pin.pCritSec);
    LeaveCriticalSection(&pFilter->filter.csFilter);
    LeaveCriticalSection(&pFilter->csRenderLock);
    return hr;
}

139 140 141 142
static HRESULT WINAPI BaseRenderer_InputPin_EndFlush(IPin * iface)
{
    BaseInputPin* This = impl_BaseInputPin_from_IPin(iface);
    BaseRenderer *pFilter = impl_from_IBaseFilter(This->pin.pinInfo.pFilter);
143
    HRESULT hr;
144 145 146 147

    TRACE("(%p/%p)->()\n", This, pFilter);

    EnterCriticalSection(&pFilter->csRenderLock);
148
    EnterCriticalSection(&pFilter->filter.csFilter);
149 150 151 152 153 154 155 156 157 158 159
    EnterCriticalSection(This->pin.pCritSec);
    hr = BaseInputPinImpl_EndFlush(iface);
    if (SUCCEEDED(hr))
    {
        if (pFilter->pFuncsTable->pfnEndFlush)
            hr = pFilter->pFuncsTable->pfnEndFlush(pFilter);
        else
            hr = BaseRendererImpl_EndFlush(pFilter);
    }
    LeaveCriticalSection(This->pin.pCritSec);
    LeaveCriticalSection(&pFilter->filter.csFilter);
160
    LeaveCriticalSection(&pFilter->csRenderLock);
161 162 163 164 165 166 167 168 169
    return hr;
}

static const IPinVtbl BaseRenderer_InputPin_Vtbl =
{
    BaseInputPinImpl_QueryInterface,
    BasePinImpl_AddRef,
    BaseInputPinImpl_Release,
    BaseInputPinImpl_Connect,
170 171
    BaseRenderer_InputPin_ReceiveConnection,
    BaseRenderer_InputPin_Disconnect,
172 173 174 175 176
    BasePinImpl_ConnectedTo,
    BasePinImpl_ConnectionMediaType,
    BasePinImpl_QueryPinInfo,
    BasePinImpl_QueryDirection,
    BasePinImpl_QueryId,
177
    BasePinImpl_QueryAccept,
178 179 180
    BasePinImpl_EnumMediaTypes,
    BasePinImpl_QueryInternalConnections,
    BaseRenderer_InputPin_EndOfStream,
181
    BaseRenderer_InputPin_BeginFlush,
182 183 184 185
    BaseRenderer_InputPin_EndFlush,
    BaseInputPinImpl_NewSegment
};

186
static IPin *renderer_get_pin(BaseFilter *iface, unsigned int index)
187 188 189
{
    BaseRenderer *This = impl_from_BaseFilter(iface);

190
    if (index >= 1)
191 192 193 194 195
        return NULL;

    return &This->pInputPin->pin.IPin_iface;
}

196 197 198 199 200 201
static void renderer_destroy(BaseFilter *iface)
{
    BaseRenderer *filter = impl_from_BaseFilter(iface);
    filter->pFuncsTable->renderer_destroy(filter);
}

202
static HRESULT renderer_query_interface(BaseFilter *iface, REFIID iid, void **out)
203
{
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224
    BaseRenderer *filter = impl_from_BaseFilter(iface);
    HRESULT hr;

    if (filter->pFuncsTable->renderer_query_interface
            && SUCCEEDED(hr = filter->pFuncsTable->renderer_query_interface(filter, iid, out)))
    {
        return hr;
    }

    if (IsEqualIID(iid, &IID_IMediaSeeking) || IsEqualIID(iid, &IID_IMediaPosition))
        return IUnknown_QueryInterface(filter->pPosition, iid, out);
    else if (IsEqualIID(iid, &IID_IQualityControl))
    {
        *out = &filter->qcimpl->IQualityControl_iface;
        IUnknown_AddRef((IUnknown *)*out);
        return S_OK;
    }
    return E_NOINTERFACE;
}

static const BaseFilterFuncTable RendererBaseFilterFuncTable = {
225 226
    .filter_get_pin = renderer_get_pin,
    .filter_destroy = renderer_destroy,
227
    .filter_query_interface = renderer_query_interface,
228 229
};

230 231 232 233 234 235 236 237 238 239 240 241 242
static HRESULT WINAPI BaseRenderer_Input_CheckMediaType(BasePin *pin, const AM_MEDIA_TYPE * pmt)
{
    BaseRenderer *This = impl_from_IBaseFilter(pin->pinInfo.pFilter);
    return This->pFuncsTable->pfnCheckMediaType(This, pmt);
}

static HRESULT WINAPI BaseRenderer_Receive(BaseInputPin *pin, IMediaSample * pSample)
{
    BaseRenderer *This = impl_from_IBaseFilter(pin->pin.pinInfo.pFilter);
    return BaseRendererImpl_Receive(This, pSample);
}

static const BaseInputPinFuncTable input_BaseInputFuncTable = {
243 244 245 246 247
    {
        BaseRenderer_Input_CheckMediaType,
        BasePinImpl_GetMediaType
    },
    BaseRenderer_Receive
248 249 250
};


251 252
HRESULT WINAPI strmbase_renderer_init(BaseRenderer *This, const IBaseFilterVtbl *vtbl,
        IUnknown *outer, const CLSID *clsid, const WCHAR *sink_name, DWORD_PTR debug_info,
253
        const BaseRendererFuncTable *pBaseFuncsTable)
254 255 256 257
{
    PIN_INFO piInput;
    HRESULT hr;

258
    strmbase_filter_init(&This->filter, vtbl, outer, clsid, debug_info, &RendererBaseFilterFuncTable);
259 260 261 262 263 264

    This->pFuncsTable = pBaseFuncsTable;

    /* construct input pin */
    piInput.dir = PINDIR_INPUT;
    piInput.pFilter = &This->filter.IBaseFilter_iface;
265
    lstrcpynW(piInput.achName, sink_name, ARRAY_SIZE(piInput.achName));
266

267
    hr = BaseInputPin_Construct(&BaseRenderer_InputPin_Vtbl, sizeof(BaseInputPin), &piInput,
268
            &input_BaseInputFuncTable, &This->filter.csFilter, NULL, (IPin **)&This->pInputPin);
269 270 271

    if (SUCCEEDED(hr))
    {
272
        hr = CreatePosPassThru(outer ? outer : (IUnknown *)&This->filter.IBaseFilter_iface, TRUE,
273
                &This->pInputPin->pin.IPin_iface, &This->pPosition);
274 275 276 277 278
        if (FAILED(hr))
            return hr;

        InitializeCriticalSection(&This->csRenderLock);
        This->csRenderLock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__": BaseRenderer.csRenderLock");
279
        This->evComplete = CreateEventW(NULL, TRUE, TRUE, NULL);
280 281 282
        This->ThreadSignal = CreateEventW(NULL, TRUE, TRUE, NULL);
        This->RenderEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
        This->pMediaSample = NULL;
283

284 285
        QualityControlImpl_Create(&This->pInputPin->pin.IPin_iface, &This->filter.IBaseFilter_iface, &This->qcimpl);
        This->qcimpl->IQualityControl_iface.lpVtbl = &Renderer_QualityControl_Vtbl;
286 287 288 289 290
    }

    return hr;
}

291
void strmbase_renderer_cleanup(BaseRenderer *filter)
292
{
293
    IPin *peer;
294

295
    if (SUCCEEDED(IPin_ConnectedTo(&filter->pInputPin->pin.IPin_iface, &peer)))
296
    {
297 298 299 300 301
        IPin_Disconnect(peer);
        IPin_Release(peer);
    }
    IPin_Disconnect(&filter->pInputPin->pin.IPin_iface);
    IPin_Release(&filter->pInputPin->pin.IPin_iface);
302

303 304 305 306 307
    if (filter->pPosition)
        IUnknown_Release(filter->pPosition);

    filter->csRenderLock.DebugInfo->Spare[0] = 0;
    DeleteCriticalSection(&filter->csRenderLock);
308

309 310 311 312 313
    BaseRendererImpl_ClearPendingSample(filter);
    CloseHandle(filter->evComplete);
    CloseHandle(filter->ThreadSignal);
    CloseHandle(filter->RenderEvent);
    QualityControlImpl_Destroy(filter->qcimpl);
314
    strmbase_filter_cleanup(&filter->filter);
315 316
}

317 318 319 320
HRESULT WINAPI BaseRendererImpl_Receive(BaseRenderer *This, IMediaSample * pSample)
{
    HRESULT hr = S_OK;
    REFERENCE_TIME start, stop;
321
    AM_MEDIA_TYPE *pmt;
322 323 324

    TRACE("(%p)->%p\n", This, pSample);

325 326 327 328 329 330 331 332 333 334 335 336
    if (This->pInputPin->end_of_stream || This->pInputPin->flushing)
        return S_FALSE;

    if (This->filter.state == State_Stopped)
        return VFW_E_WRONG_STATE;

    if (IMediaSample_GetMediaType(pSample, &pmt) == S_OK)
    {
        if (FAILED(This->pFuncsTable->pfnCheckMediaType(This, pmt)))
        {
            return VFW_E_TYPE_NOT_ACCEPTED;
        }
337
        DeleteMediaType(pmt);
338 339
    }

340 341 342
    This->pMediaSample = pSample;
    IMediaSample_AddRef(pSample);

343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360
    if (This->pFuncsTable->pfnPrepareReceive)
        hr = This->pFuncsTable->pfnPrepareReceive(This, pSample);
    if (FAILED(hr))
    {
        if (hr == VFW_E_SAMPLE_REJECTED)
            return S_OK;
        else
            return hr;
    }

    if (This->pFuncsTable->pfnPrepareRender)
        This->pFuncsTable->pfnPrepareRender(This);

    EnterCriticalSection(&This->csRenderLock);
    if ( This->filter.state == State_Paused )
    {
        if (This->pFuncsTable->pfnOnReceiveFirstSample)
            This->pFuncsTable->pfnOnReceiveFirstSample(This, pSample);
361 362

        SetEvent(This->evComplete);
363 364 365
    }

    /* Wait for render Time */
366
    if (SUCCEEDED(IMediaSample_GetTime(pSample, &start, &stop)))
367 368 369 370 371 372 373 374 375 376 377 378
    {
        hr = S_FALSE;
        RendererPosPassThru_RegisterMediaTime(This->pPosition, start);
        if (This->pFuncsTable->pfnShouldDrawSampleNow)
            hr = This->pFuncsTable->pfnShouldDrawSampleNow(This, pSample, &start, &stop);

        if (hr == S_OK)
            ;/* Do not wait: drop through */
        else if (hr == S_FALSE)
        {
            if (This->pFuncsTable->pfnOnWaitStart)
                This->pFuncsTable->pfnOnWaitStart(This);
379

380
            LeaveCriticalSection(&This->csRenderLock);
381
            hr = QualityControlRender_WaitFor(This->qcimpl, pSample, This->RenderEvent);
382
            EnterCriticalSection(&This->csRenderLock);
383

384 385 386 387 388 389 390 391 392 393 394 395
            if (This->pFuncsTable->pfnOnWaitEnd)
                This->pFuncsTable->pfnOnWaitEnd(This);
        }
        else
        {
            LeaveCriticalSection(&This->csRenderLock);
            /* Drop Sample */
            return S_OK;
        }
    }

    if (SUCCEEDED(hr))
396
    {
397
        QualityControlRender_BeginRender(This->qcimpl);
398
        hr = This->pFuncsTable->pfnDoRenderSample(This, pSample);
399
        QualityControlRender_EndRender(This->qcimpl);
400 401
    }

402
    QualityControlRender_DoQOS(This->qcimpl);
403 404

    BaseRendererImpl_ClearPendingSample(This);
405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421
    LeaveCriticalSection(&This->csRenderLock);

    return hr;
}

HRESULT WINAPI BaseRendererImpl_Stop(IBaseFilter * iface)
{
    BaseRenderer *This = impl_from_IBaseFilter(iface);

    TRACE("(%p)->()\n", This);

    EnterCriticalSection(&This->csRenderLock);
    {
        RendererPosPassThru_ResetMediaTime(This->pPosition);
        if (This->pFuncsTable->pfnOnStopStreaming)
            This->pFuncsTable->pfnOnStopStreaming(This);
        This->filter.state = State_Stopped;
422
        SetEvent(This->evComplete);
423 424
        SetEvent(This->ThreadSignal);
        SetEvent(This->RenderEvent);
425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440
    }
    LeaveCriticalSection(&This->csRenderLock);

    return S_OK;
}

HRESULT WINAPI BaseRendererImpl_Run(IBaseFilter * iface, REFERENCE_TIME tStart)
{
    HRESULT hr = S_OK;
    BaseRenderer *This = impl_from_IBaseFilter(iface);
    TRACE("(%p)->(%s)\n", This, wine_dbgstr_longlong(tStart));

    EnterCriticalSection(&This->csRenderLock);
    This->filter.rtStreamStart = tStart;
    if (This->filter.state == State_Running)
        goto out;
441 442

    SetEvent(This->evComplete);
443
    ResetEvent(This->ThreadSignal);
444

445 446
    if (This->pInputPin->pin.pConnectedTo)
    {
447
        This->pInputPin->end_of_stream = FALSE;
448 449 450 451 452 453 454 455 456 457 458 459 460 461
    }
    else if (This->filter.filterInfo.pGraph)
    {
        IMediaEventSink *pEventSink;
        hr = IFilterGraph_QueryInterface(This->filter.filterInfo.pGraph, &IID_IMediaEventSink, (LPVOID*)&pEventSink);
        if (SUCCEEDED(hr))
        {
            hr = IMediaEventSink_Notify(pEventSink, EC_COMPLETE, S_OK, (LONG_PTR)This);
            IMediaEventSink_Release(pEventSink);
        }
        hr = S_OK;
    }
    if (SUCCEEDED(hr))
    {
462
        QualityControlRender_Start(This->qcimpl, This->filter.rtStreamStart);
463 464
        if (This->pFuncsTable->pfnOnStartStreaming)
            This->pFuncsTable->pfnOnStartStreaming(This);
465 466 467
        if (This->filter.state == State_Stopped)
            BaseRendererImpl_ClearPendingSample(This);
        SetEvent(This->RenderEvent);
468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486
        This->filter.state = State_Running;
    }
out:
    LeaveCriticalSection(&This->csRenderLock);

    return hr;
}

HRESULT WINAPI BaseRendererImpl_Pause(IBaseFilter * iface)
{
    BaseRenderer *This = impl_from_IBaseFilter(iface);

    TRACE("(%p)->()\n", This);

    EnterCriticalSection(&This->csRenderLock);
    {
     if (This->filter.state != State_Paused)
        {
            if (This->filter.state == State_Stopped)
487 488 489
            {
                if (This->pInputPin->pin.pConnectedTo)
                    ResetEvent(This->evComplete);
490
                This->pInputPin->end_of_stream = FALSE;
491 492 493
            }
            else if (This->pFuncsTable->pfnOnStopStreaming)
                This->pFuncsTable->pfnOnStopStreaming(This);
494 495 496 497

            if (This->filter.state == State_Stopped)
                BaseRendererImpl_ClearPendingSample(This);
            ResetEvent(This->RenderEvent);
498 499 500
            This->filter.state = State_Paused;
        }
    }
501
    ResetEvent(This->ThreadSignal);
502 503 504 505 506
    LeaveCriticalSection(&This->csRenderLock);

    return S_OK;
}

507 508 509 510 511 512
HRESULT WINAPI BaseRendererImpl_SetSyncSource(IBaseFilter *iface, IReferenceClock *clock)
{
    BaseRenderer *This = impl_from_IBaseFilter(iface);
    HRESULT hr;

    EnterCriticalSection(&This->filter.csFilter);
513
    QualityControlRender_SetClock(This->qcimpl, clock);
514 515 516 517 518 519
    hr = BaseFilterImpl_SetSyncSource(iface, clock);
    LeaveCriticalSection(&This->filter.csFilter);
    return hr;
}


520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536
HRESULT WINAPI BaseRendererImpl_GetState(IBaseFilter * iface, DWORD dwMilliSecsTimeout, FILTER_STATE *pState)
{
    HRESULT hr;
    BaseRenderer *This = impl_from_IBaseFilter(iface);

    TRACE("(%p)->(%d, %p)\n", This, dwMilliSecsTimeout, pState);

    if (WaitForSingleObject(This->evComplete, dwMilliSecsTimeout) == WAIT_TIMEOUT)
        hr = VFW_S_STATE_INTERMEDIATE;
    else
        hr = S_OK;

    BaseFilterImpl_GetState(iface, dwMilliSecsTimeout, pState);

    return hr;
}

537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554
HRESULT WINAPI BaseRendererImpl_EndOfStream(BaseRenderer* iface)
{
    IMediaEventSink* pEventSink;
    IFilterGraph *graph;
    HRESULT hr = S_OK;

    TRACE("(%p)\n", iface);

    graph = iface->filter.filterInfo.pGraph;
    if (graph)
    {        hr = IFilterGraph_QueryInterface(iface->filter.filterInfo.pGraph, &IID_IMediaEventSink, (LPVOID*)&pEventSink);
        if (SUCCEEDED(hr))
        {
            hr = IMediaEventSink_Notify(pEventSink, EC_COMPLETE, S_OK, (LONG_PTR)iface);
            IMediaEventSink_Release(pEventSink);
        }
    }
    RendererPosPassThru_EOS(iface->pPosition);
555
    SetEvent(iface->evComplete);
556 557 558 559

    return hr;
}

560 561 562
HRESULT WINAPI BaseRendererImpl_BeginFlush(BaseRenderer* iface)
{
    TRACE("(%p)\n", iface);
563 564 565
    BaseRendererImpl_ClearPendingSample(iface);
    SetEvent(iface->ThreadSignal);
    SetEvent(iface->RenderEvent);
566 567 568
    return S_OK;
}

569 570 571
HRESULT WINAPI BaseRendererImpl_EndFlush(BaseRenderer* iface)
{
    TRACE("(%p)\n", iface);
572
    QualityControlRender_Start(iface->qcimpl, iface->filter.rtStreamStart);
573
    RendererPosPassThru_ResetMediaTime(iface->pPosition);
574 575 576 577 578 579 580 581 582 583 584 585
    ResetEvent(iface->ThreadSignal);
    ResetEvent(iface->RenderEvent);
    return S_OK;
}

HRESULT WINAPI BaseRendererImpl_ClearPendingSample(BaseRenderer *iface)
{
    if (iface->pMediaSample)
    {
        IMediaSample_Release(iface->pMediaSample);
        iface->pMediaSample = NULL;
    }
586 587
    return S_OK;
}