transform.c 19.3 KB
Newer Older
1 2 3 4
/*
 * Transform Filter (Base for decoders, etc...)
 *
 * Copyright 2005 Christian Costa
5
 * Copyright 2010 Aric Stewart, CodeWeavers
6 7 8 9 10 11 12 13 14 15 16 17 18
 *
 * 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
19
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 21
 */
#include "config.h"
22
#include <stdarg.h>
23

24
#define COBJMACROS
25 26 27 28

#include "windef.h"
#include "winbase.h"
#include "dshow.h"
29
#include "amvideo.h"
30 31 32 33 34 35 36
#include "strmif.h"
#include "vfw.h"

#include <assert.h>

#include "wine/unicode.h"
#include "wine/debug.h"
37
#include "wine/strmbase.h"
38
#include "strmbase_private.h"
39

40
WINE_DEFAULT_DEBUG_CHANNEL(strmbase);
41 42 43 44 45 46 47

static const WCHAR wcsInputPinName[] = {'i','n','p','u','t',' ','p','i','n',0};
static const WCHAR wcsOutputPinName[] = {'o','u','t','p','u','t',' ','p','i','n',0};

static const IBaseFilterVtbl TransformFilter_Vtbl;
static const IPinVtbl TransformFilter_InputPin_Vtbl;
static const IPinVtbl TransformFilter_OutputPin_Vtbl;
48
static const IQualityControlVtbl TransformFilter_QualityControl_Vtbl;
49

50 51 52 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
static inline BaseInputPin *impl_BaseInputPin_from_BasePin( BasePin *iface )
{
    return CONTAINING_RECORD(iface, BaseInputPin, pin);
}

static inline BasePin *impl_BasePin_from_IPin( IPin *iface )
{
    return CONTAINING_RECORD(iface, BasePin, IPin_iface);
}

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

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

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

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

80
static HRESULT WINAPI TransformFilter_Input_CheckMediaType(BasePin *iface, const AM_MEDIA_TYPE * pmt)
81
{
82
    BaseInputPin* This = impl_BaseInputPin_from_BasePin(iface);
83 84
    TransformFilter * pTransform;

85
    TRACE("%p\n", iface);
86
    pTransform = impl_from_IBaseFilter(This->pin.pinInfo.pFilter);
87

88 89
    if (pTransform->pFuncsTable->pfnCheckInputType)
        return pTransform->pFuncsTable->pfnCheckInputType(pTransform, pmt);
90 91
    /* Assume OK if there's no query method (the connection will fail if
       needed) */
92
    return S_OK;
93 94
}

95
static HRESULT WINAPI TransformFilter_Input_Receive(BaseInputPin *This, IMediaSample *pInSample)
96 97 98
{
    HRESULT hr = S_FALSE;
    TransformFilter * pTransform;
99
    TRACE("%p\n", This);
100
    pTransform = impl_from_IBaseFilter(This->pin.pinInfo.pFilter);
101

102
    EnterCriticalSection(&pTransform->csReceive);
103 104
    if (pTransform->filter.state == State_Stopped)
    {
105
        LeaveCriticalSection(&pTransform->csReceive);
106 107 108 109 110
        return VFW_E_WRONG_STATE;
    }

    if (This->end_of_stream || This->flushing)
    {
111
        LeaveCriticalSection(&pTransform->csReceive);
112 113 114
        return S_FALSE;
    }

115
    LeaveCriticalSection(&pTransform->csReceive);
116 117 118 119 120 121 122
    if (pTransform->pFuncsTable->pfnReceive)
        hr = pTransform->pFuncsTable->pfnReceive(pTransform, pInSample);
    else
        hr = S_FALSE;

    return hr;
}
123

124
static HRESULT WINAPI TransformFilter_Output_QueryAccept(IPin *iface, const AM_MEDIA_TYPE * pmt)
125
{
126 127
    BasePin *This = impl_BasePin_from_IPin(iface);
    TransformFilter *pTransformFilter = impl_from_IBaseFilter(This->pinInfo.pFilter);
128
    AM_MEDIA_TYPE* outpmt = &pTransformFilter->pmt;
129 130
    TRACE("%p\n", iface);

131 132
    if (IsEqualIID(&pmt->majortype, &outpmt->majortype)
        && (IsEqualIID(&pmt->subtype, &outpmt->subtype) || IsEqualIID(&outpmt->subtype, &GUID_NULL)))
133 134 135 136
        return S_OK;
    return S_FALSE;
}

137
static HRESULT WINAPI TransformFilter_Output_DecideBufferSize(BaseOutputPin *This, IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *ppropInputRequest)
138
{
139
    TransformFilter *pTransformFilter = impl_from_IBaseFilter(This->pin.pinInfo.pFilter);
140 141 142
    return pTransformFilter->pFuncsTable->pfnDecideBufferSize(pTransformFilter, pAlloc, ppropInputRequest);
}

143 144
static HRESULT WINAPI TransformFilter_Output_GetMediaType(BasePin *This, int iPosition, AM_MEDIA_TYPE *pmt)
{
145
    TransformFilter *pTransform = impl_from_IBaseFilter(This->pinInfo.pFilter);
146 147 148 149 150 151 152 153 154

    if (iPosition < 0)
        return E_INVALIDARG;
    if (iPosition > 0)
        return VFW_S_NO_MORE_ITEMS;
    CopyMediaType(pmt, &pTransform->pmt);
    return S_OK;
}

155
static IPin* WINAPI TransformFilter_GetPin(BaseFilter *iface, int pos)
156
{
157
    TransformFilter *This = impl_from_BaseFilter(iface);
158 159 160 161 162 163 164 165

    if (pos >= This->npins || pos < 0)
        return NULL;

    IPin_AddRef(This->ppPins[pos]);
    return This->ppPins[pos];
}

166
static LONG WINAPI TransformFilter_GetPinCount(BaseFilter *iface)
167
{
168
    TransformFilter *This = impl_from_BaseFilter(iface);
169 170 171 172

    return (This->npins+1);
}

173 174 175 176 177 178 179 180
static const BaseFilterFuncTable tfBaseFuncTable = {
    TransformFilter_GetPin,
    TransformFilter_GetPinCount
};

static const  BasePinFuncTable tf_input_BaseFuncTable = {
    TransformFilter_Input_CheckMediaType,
    NULL,
181 182
    BasePinImpl_GetMediaTypeVersion,
    BasePinImpl_GetMediaType
183 184 185 186 187 188 189 190 191
};

static const BaseInputPinFuncTable tf_input_BaseInputFuncTable = {
    TransformFilter_Input_Receive
};

static const  BasePinFuncTable tf_output_BaseFuncTable = {
    NULL,
    BaseOutputPinImpl_AttemptConnection,
192 193
    BasePinImpl_GetMediaTypeVersion,
    TransformFilter_Output_GetMediaType
194 195 196
};

static const BaseOutputPinFuncTable tf_output_BaseOutputFuncTable = {
197 198 199
    TransformFilter_Output_DecideBufferSize,
    BaseOutputPinImpl_DecideAllocator,
    BaseOutputPinImpl_BreakConnect
200 201
};

202
static HRESULT TransformFilter_Init(const IBaseFilterVtbl *pVtbl, const CLSID* pClsid, const TransformFilterFuncTable* pFuncsTable, TransformFilter* pTransformFilter)
203 204 205 206 207
{
    HRESULT hr;
    PIN_INFO piInput;
    PIN_INFO piOutput;

208
    BaseFilter_Init(&pTransformFilter->filter, pVtbl, pClsid, (DWORD_PTR)(__FILE__ ": TransformFilter.csFilter"), &tfBaseFuncTable);
209

210 211 212
    InitializeCriticalSection(&pTransformFilter->csReceive);
    pTransformFilter->csReceive.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__": TransformFilter.csReceive");

213
    /* pTransformFilter is already allocated */
Christian Costa's avatar
Christian Costa committed
214
    pTransformFilter->pFuncsTable = pFuncsTable;
215
    ZeroMemory(&pTransformFilter->pmt, sizeof(pTransformFilter->pmt));
216
    pTransformFilter->npins = 2;
217 218 219 220 221

    pTransformFilter->ppPins = CoTaskMemAlloc(2 * sizeof(IPin *));

    /* construct input pin */
    piInput.dir = PINDIR_INPUT;
222
    piInput.pFilter = &pTransformFilter->filter.IBaseFilter_iface;
223
    lstrcpynW(piInput.achName, wcsInputPinName, sizeof(piInput.achName) / sizeof(piInput.achName[0]));
224
    piOutput.dir = PINDIR_OUTPUT;
225
    piOutput.pFilter = &pTransformFilter->filter.IBaseFilter_iface;
226
    lstrcpynW(piOutput.achName, wcsOutputPinName, sizeof(piOutput.achName) / sizeof(piOutput.achName[0]));
227

228
    hr = BaseInputPin_Construct(&TransformFilter_InputPin_Vtbl, &piInput, &tf_input_BaseFuncTable, &tf_input_BaseInputFuncTable, &pTransformFilter->filter.csFilter, NULL, &pTransformFilter->ppPins[0]);
229 230 231

    if (SUCCEEDED(hr))
    {
232
        hr = BaseOutputPin_Construct(&TransformFilter_OutputPin_Vtbl, sizeof(BaseOutputPin), &piOutput, &tf_output_BaseFuncTable, &tf_output_BaseOutputFuncTable, &pTransformFilter->filter.csFilter, &pTransformFilter->ppPins[1]);
233

234 235
        if (FAILED(hr))
            ERR("Cannot create output pin (%x)\n", hr);
236
        else {
237
            QualityControlImpl_Create( pTransformFilter->ppPins[0], &pTransformFilter->filter.IBaseFilter_iface, &pTransformFilter->qcimpl);
238
            pTransformFilter->qcimpl->IQualityControl_iface.lpVtbl = &TransformFilter_QualityControl_Vtbl;
239
        }
240
    }
241
    if (FAILED(hr))
242 243
    {
        CoTaskMemFree(pTransformFilter->ppPins);
244
        BaseFilterImpl_Release(&pTransformFilter->filter.IBaseFilter_iface);
245 246 247 248 249
    }

    return hr;
}

250
HRESULT TransformFilter_Construct(const IBaseFilterVtbl *pVtbl, LONG filter_size, const CLSID* pClsid, const TransformFilterFuncTable* pFuncsTable, IBaseFilter ** ppTransformFilter)
251
{
252 253 254 255 256 257 258 259 260 261 262
    TransformFilter* pTf;

    *ppTransformFilter = NULL;

    assert(filter_size >= sizeof(TransformFilter));

    pTf = CoTaskMemAlloc(filter_size);

    if (!pTf)
        return E_OUTOFMEMORY;

263 264
    ZeroMemory(pTf, filter_size);

265
    if (SUCCEEDED(TransformFilter_Init(pVtbl, pClsid, pFuncsTable, pTf)))
266
    {
267
        *ppTransformFilter = &pTf->filter.IBaseFilter_iface;
268 269 270
        return S_OK;
    }

271 272 273
    CoTaskMemFree(pTf);
    return E_FAIL;
}
274

275 276 277
HRESULT WINAPI TransformFilterImpl_QueryInterface(IBaseFilter * iface, REFIID riid, LPVOID * ppv)
{
    HRESULT hr;
278
    TransformFilter *This = impl_from_IBaseFilter(iface);
279 280
    TRACE("(%p/%p)->(%s, %p)\n", This, iface, debugstr_guid(riid), ppv);

281
    if (IsEqualIID(riid, &IID_IQualityControl))  {
282
        *ppv = (IQualityControl*)This->qcimpl;
283 284 285
        IUnknown_AddRef((IUnknown*)*ppv);
        return S_OK;
    }
286 287
    hr = BaseFilterImpl_QueryInterface(iface, riid, ppv);

288 289
    if (FAILED(hr) && !IsEqualIID(riid, &IID_IPin) && !IsEqualIID(riid, &IID_IVideoWindow) &&
        !IsEqualIID(riid, &IID_IAMFilterMiscFlags))
290 291 292
        FIXME("No interface for %s!\n", debugstr_guid(riid));

    return hr;
293 294
}

295
ULONG WINAPI TransformFilterImpl_Release(IBaseFilter * iface)
296
{
297
    TransformFilter *This = impl_from_IBaseFilter(iface);
298
    ULONG refCount = BaseFilterImpl_Release(iface);
299

300
    TRACE("(%p/%p)->() Release from %d\n", This, iface, refCount + 1);
301 302 303 304 305

    if (!refCount)
    {
        ULONG i;

306
        for (i = 0; i < This->npins; i++)
307 308 309 310 311 312 313 314 315 316
        {
            IPin *pConnectedTo;

            if (SUCCEEDED(IPin_ConnectedTo(This->ppPins[i], &pConnectedTo)))
            {
                IPin_Disconnect(pConnectedTo);
                IPin_Release(pConnectedTo);
            }
            IPin_Disconnect(This->ppPins[i]);

317
            IPin_Release(This->ppPins[i]);
318
        }
319

320
        CoTaskMemFree(This->ppPins);
321

322
        TRACE("Destroying transform filter\n");
323 324
        This->csReceive.DebugInfo->Spare[0] = 0;
        DeleteCriticalSection(&This->csReceive);
325
        FreeMediaType(&This->pmt);
326
        QualityControlImpl_Destroy(This->qcimpl);
327 328 329 330 331 332 333 334 335 336
        CoTaskMemFree(This);

        return 0;
    }
    else
        return refCount;
}

/** IMediaFilter methods **/

337
HRESULT WINAPI TransformFilterImpl_Stop(IBaseFilter * iface)
338
{
339
    TransformFilter *This = impl_from_IBaseFilter(iface);
340
    HRESULT hr = S_OK;
341 342 343

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

344
    EnterCriticalSection(&This->csReceive);
345
    {
346
        This->filter.state = State_Stopped;
347 348
        if (This->pFuncsTable->pfnStopStreaming)
            hr = This->pFuncsTable->pfnStopStreaming(This);
349
    }
350
    LeaveCriticalSection(&This->csReceive);
351

352
    return hr;
353 354
}

355
HRESULT WINAPI TransformFilterImpl_Pause(IBaseFilter * iface)
356
{
357
    TransformFilter *This = impl_from_IBaseFilter(iface);
358
    HRESULT hr;
359 360 361

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

362
    EnterCriticalSection(&This->csReceive);
363
    {
364
        if (This->filter.state == State_Stopped)
365 366 367
            hr = IBaseFilter_Run(iface, -1);
        else
            hr = S_OK;
368

369
        if (SUCCEEDED(hr))
370
            This->filter.state = State_Paused;
371
    }
372
    LeaveCriticalSection(&This->csReceive);
373

374
    return hr;
375 376
}

377
HRESULT WINAPI TransformFilterImpl_Run(IBaseFilter * iface, REFERENCE_TIME tStart)
378 379
{
    HRESULT hr = S_OK;
380
    TransformFilter *This = impl_from_IBaseFilter(iface);
381 382 383

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

384
    EnterCriticalSection(&This->csReceive);
385
    {
386
        if (This->filter.state == State_Stopped)
387
        {
388
            impl_BaseInputPin_from_IPin(This->ppPins[0])->end_of_stream = 0;
389 390
            if (This->pFuncsTable->pfnStartStreaming)
                hr = This->pFuncsTable->pfnStartStreaming(This);
391
            if (SUCCEEDED(hr))
392
                hr = BaseOutputPinImpl_Active(impl_BaseOutputPin_from_IPin(This->ppPins[1]));
393
        }
394

395 396
        if (SUCCEEDED(hr))
        {
397 398
            This->filter.rtStreamStart = tStart;
            This->filter.state = State_Running;
399
        }
400
    }
401
    LeaveCriticalSection(&This->csReceive);
402 403 404 405

    return hr;
}

406 407
HRESULT WINAPI TransformFilterImpl_Notify(TransformFilter *iface, IBaseFilter *sender, Quality qm)
{
408
    return QualityControlImpl_Notify((IQualityControl*)iface->qcimpl, sender, qm);
409 410
}

411 412
/** IBaseFilter implementation **/

413
HRESULT WINAPI TransformFilterImpl_FindPin(IBaseFilter * iface, LPCWSTR Id, IPin **ppPin)
414
{
415
    TransformFilter *This = impl_from_IBaseFilter(iface);
416

417
    TRACE("(%p/%p)->(%s,%p)\n", This, iface, debugstr_w(Id), ppPin);
418 419 420 421 422 423

    return E_NOTIMPL;
}

static const IBaseFilterVtbl TransformFilter_Vtbl =
{
424
    TransformFilterImpl_QueryInterface,
425
    BaseFilterImpl_AddRef,
426
    TransformFilterImpl_Release,
427
    BaseFilterImpl_GetClassID,
428 429 430
    TransformFilterImpl_Stop,
    TransformFilterImpl_Pause,
    TransformFilterImpl_Run,
431 432 433
    BaseFilterImpl_GetState,
    BaseFilterImpl_SetSyncSource,
    BaseFilterImpl_GetSyncSource,
434
    BaseFilterImpl_EnumPins,
435
    TransformFilterImpl_FindPin,
436 437 438
    BaseFilterImpl_QueryFilterInfo,
    BaseFilterImpl_JoinFilterGraph,
    BaseFilterImpl_QueryVendorInfo
439 440
};

441
static HRESULT WINAPI TransformFilter_InputPin_EndOfStream(IPin * iface)
442
{
443
    BaseInputPin* This = impl_BaseInputPin_from_IPin(iface);
444
    TransformFilter* pTransform;
445 446
    IPin* ppin;
    HRESULT hr;
447

448 449 450
    TRACE("(%p)->()\n", iface);

    /* Since we process samples synchronously, just forward notification downstream */
451
    pTransform = impl_from_IBaseFilter(This->pin.pinInfo.pFilter);
452 453 454 455 456 457 458 459 460 461 462
    if (!pTransform)
        hr = E_FAIL;
    else
        hr = IPin_ConnectedTo(pTransform->ppPins[1], &ppin);
    if (SUCCEEDED(hr))
    {
        hr = IPin_EndOfStream(ppin);
        IPin_Release(ppin);
    }

    if (FAILED(hr))
463
        ERR("%x\n", hr);
464 465 466
    return hr;
}

467 468
static HRESULT WINAPI TransformFilter_InputPin_ReceiveConnection(IPin * iface, IPin * pReceivePin, const AM_MEDIA_TYPE * pmt)
{
469
    BaseInputPin* This = impl_BaseInputPin_from_IPin(iface);
470 471
    TransformFilter* pTransform;
    HRESULT hr = S_OK;
472 473 474

    TRACE("(%p)->(%p, %p)\n", iface, pReceivePin, pmt);

475
    pTransform = impl_from_IBaseFilter(This->pin.pinInfo.pFilter);
476 477 478 479 480 481

    if (pTransform->pFuncsTable->pfnSetMediaType)
        hr = pTransform->pFuncsTable->pfnSetMediaType(pTransform, PINDIR_INPUT, pmt);

    if (SUCCEEDED(hr) && pTransform->pFuncsTable->pfnCompleteConnect)
        hr = pTransform->pFuncsTable->pfnCompleteConnect(pTransform, PINDIR_INPUT, pReceivePin);
482 483 484

    if (SUCCEEDED(hr))
    {
485
        hr = BaseInputPinImpl_ReceiveConnection(iface, pReceivePin, pmt);
486 487
        if (FAILED(hr) && pTransform->pFuncsTable->pfnBreakConnect)
            pTransform->pFuncsTable->pfnBreakConnect(pTransform, PINDIR_INPUT);
488 489 490 491 492 493 494
    }

    return hr;
}

static HRESULT WINAPI TransformFilter_InputPin_Disconnect(IPin * iface)
{
495
    BaseInputPin* This = impl_BaseInputPin_from_IPin(iface);
496
    TransformFilter* pTransform;
497 498 499

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

500
    pTransform = impl_from_IBaseFilter(This->pin.pinInfo.pFilter);
501 502
    if (pTransform->pFuncsTable->pfnBreakConnect)
        pTransform->pFuncsTable->pfnBreakConnect(pTransform, PINDIR_INPUT);
503

504
    return BasePinImpl_Disconnect(iface);
505 506
}

507 508
static HRESULT WINAPI TransformFilter_InputPin_BeginFlush(IPin * iface)
{
509
    BaseInputPin* This = impl_BaseInputPin_from_IPin(iface);
510
    TransformFilter* pTransform;
511 512 513 514
    HRESULT hr = S_OK;

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

515
    pTransform = impl_from_IBaseFilter(This->pin.pinInfo.pFilter);
516
    EnterCriticalSection(&pTransform->filter.csFilter);
517
    if (pTransform->pFuncsTable->pfnBeginFlush)
518
        hr = pTransform->pFuncsTable->pfnBeginFlush(pTransform);
519
    if (SUCCEEDED(hr))
520
        hr = BaseInputPinImpl_BeginFlush(iface);
521
    LeaveCriticalSection(&pTransform->filter.csFilter);
522 523 524 525 526
    return hr;
}

static HRESULT WINAPI TransformFilter_InputPin_EndFlush(IPin * iface)
{
527
    BaseInputPin* This = impl_BaseInputPin_from_IPin(iface);
528
    TransformFilter* pTransform;
529 530 531 532
    HRESULT hr = S_OK;

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

533
    pTransform = impl_from_IBaseFilter(This->pin.pinInfo.pFilter);
534
    EnterCriticalSection(&pTransform->filter.csFilter);
535
    if (pTransform->pFuncsTable->pfnEndFlush)
536
        hr = pTransform->pFuncsTable->pfnEndFlush(pTransform);
537
    if (SUCCEEDED(hr))
538
        hr = BaseInputPinImpl_EndFlush(iface);
539
    LeaveCriticalSection(&pTransform->filter.csFilter);
540 541 542 543 544
    return hr;
}

static HRESULT WINAPI TransformFilter_InputPin_NewSegment(IPin * iface, REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate)
{
545
    BaseInputPin* This = impl_BaseInputPin_from_IPin(iface);
546
    TransformFilter* pTransform;
547 548 549 550
    HRESULT hr = S_OK;

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

551
    pTransform = impl_from_IBaseFilter(This->pin.pinInfo.pFilter);
552
    EnterCriticalSection(&pTransform->filter.csFilter);
553
    if (pTransform->pFuncsTable->pfnNewSegment)
554
        hr = pTransform->pFuncsTable->pfnNewSegment(pTransform, tStart, tStop, dRate);
555
    if (SUCCEEDED(hr))
556
        hr = BaseInputPinImpl_NewSegment(iface, tStart, tStop, dRate);
557
    LeaveCriticalSection(&pTransform->filter.csFilter);
558 559 560
    return hr;
}

561
static const IPinVtbl TransformFilter_InputPin_Vtbl =
562
{
563
    BaseInputPinImpl_QueryInterface,
564
    BasePinImpl_AddRef,
565 566
    BaseInputPinImpl_Release,
    BaseInputPinImpl_Connect,
567 568
    TransformFilter_InputPin_ReceiveConnection,
    TransformFilter_InputPin_Disconnect,
569 570 571 572 573
    BasePinImpl_ConnectedTo,
    BasePinImpl_ConnectionMediaType,
    BasePinImpl_QueryPinInfo,
    BasePinImpl_QueryDirection,
    BasePinImpl_QueryId,
574
    BaseInputPinImpl_QueryAccept,
575 576
    BasePinImpl_EnumMediaTypes,
    BasePinImpl_QueryInternalConnections,
577
    TransformFilter_InputPin_EndOfStream,
578 579 580
    TransformFilter_InputPin_BeginFlush,
    TransformFilter_InputPin_EndFlush,
    TransformFilter_InputPin_NewSegment
581 582
};

583
static const IPinVtbl TransformFilter_OutputPin_Vtbl =
584
{
585
    BaseOutputPinImpl_QueryInterface,
586
    BasePinImpl_AddRef,
587 588 589 590
    BaseOutputPinImpl_Release,
    BaseOutputPinImpl_Connect,
    BaseOutputPinImpl_ReceiveConnection,
    BaseOutputPinImpl_Disconnect,
591 592 593 594 595 596
    BasePinImpl_ConnectedTo,
    BasePinImpl_ConnectionMediaType,
    BasePinImpl_QueryPinInfo,
    BasePinImpl_QueryDirection,
    BasePinImpl_QueryId,
    TransformFilter_Output_QueryAccept,
597
    BasePinImpl_EnumMediaTypes,
598
    BasePinImpl_QueryInternalConnections,
599 600 601
    BaseOutputPinImpl_EndOfStream,
    BaseOutputPinImpl_BeginFlush,
    BaseOutputPinImpl_EndFlush,
602
    BasePinImpl_NewSegment
603
};
604

605
static HRESULT WINAPI TransformFilter_QualityControlImpl_Notify(IQualityControl *iface, IBaseFilter *sender, Quality qm) {
606
    QualityControlImpl *qc = (QualityControlImpl*)iface;
607
    TransformFilter *This = impl_from_IBaseFilter(qc->self);
608 609 610 611

    if (This->pFuncsTable->pfnNotify)
        return This->pFuncsTable->pfnNotify(This, sender, qm);
    else
612
        return TransformFilterImpl_Notify(This, sender, qm);
613 614 615 616 617 618 619 620 621
}

static const IQualityControlVtbl TransformFilter_QualityControl_Vtbl = {
    QualityControlImpl_QueryInterface,
    QualityControlImpl_AddRef,
    QualityControlImpl_Release,
    TransformFilter_QualityControlImpl_Notify,
    QualityControlImpl_SetSink
};