msvcrt: Don't flush stream in fputs function.
[wine] / dlls / strmbase / transform.c
1 /*
2  * Transform Filter (Base for decoders, etc...)
3  *
4  * Copyright 2005 Christian Costa
5  * Copyright 2010 Aric Stewart, CodeWeavers
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * 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 #include "windef.h"
27 #include "winbase.h"
28 #include "dshow.h"
29 #include "amvideo.h"
30 #include "strmif.h"
31 #include "vfw.h"
32
33 #include <assert.h>
34
35 #include "wine/unicode.h"
36 #include "wine/debug.h"
37 #include "wine/strmbase.h"
38 #include "strmbase_private.h"
39
40 WINE_DEFAULT_DEBUG_CHANNEL(strmbase);
41
42 static const WCHAR wcsInputPinName[] = {'i','n','p','u','t',' ','p','i','n',0};
43 static const WCHAR wcsOutputPinName[] = {'o','u','t','p','u','t',' ','p','i','n',0};
44
45 static const IBaseFilterVtbl TransformFilter_Vtbl;
46 static const IPinVtbl TransformFilter_InputPin_Vtbl;
47 static const IPinVtbl TransformFilter_OutputPin_Vtbl;
48 static const IQualityControlVtbl TransformFilter_QualityControl_Vtbl;
49
50 static inline BaseInputPin *impl_BaseInputPin_from_BasePin( BasePin *iface )
51 {
52     return CONTAINING_RECORD(iface, BaseInputPin, pin);
53 }
54
55 static inline BasePin *impl_BasePin_from_IPin( IPin *iface )
56 {
57     return CONTAINING_RECORD(iface, BasePin, IPin_iface);
58 }
59
60 static inline BaseInputPin *impl_BaseInputPin_from_IPin( IPin *iface )
61 {
62     return CONTAINING_RECORD(iface, BaseInputPin, pin.IPin_iface);
63 }
64
65 static inline BaseOutputPin *impl_BaseOutputPin_from_IPin( IPin *iface )
66 {
67     return CONTAINING_RECORD(iface, BaseOutputPin, pin.IPin_iface);
68 }
69
70 static inline TransformFilter *impl_from_IBaseFilter( IBaseFilter *iface )
71 {
72     return CONTAINING_RECORD(iface, TransformFilter, filter.IBaseFilter_iface);
73 }
74
75 static inline TransformFilter *impl_from_BaseFilter( BaseFilter *iface )
76 {
77     return CONTAINING_RECORD(iface, TransformFilter, filter);
78 }
79
80 static HRESULT WINAPI TransformFilter_Input_CheckMediaType(BasePin *iface, const AM_MEDIA_TYPE * pmt)
81 {
82     BaseInputPin* This = impl_BaseInputPin_from_BasePin(iface);
83     TransformFilter * pTransform;
84
85     TRACE("%p\n", iface);
86     pTransform = impl_from_IBaseFilter(This->pin.pinInfo.pFilter);
87
88     if (pTransform->pFuncsTable->pfnCheckInputType)
89         return pTransform->pFuncsTable->pfnCheckInputType(pTransform, pmt);
90     /* Assume OK if there's no query method (the connection will fail if
91        needed) */
92     return S_OK;
93 }
94
95 static HRESULT WINAPI TransformFilter_Input_Receive(BaseInputPin *This, IMediaSample *pInSample)
96 {
97     HRESULT hr = S_FALSE;
98     TransformFilter * pTransform;
99     TRACE("%p\n", This);
100     pTransform = impl_from_IBaseFilter(This->pin.pinInfo.pFilter);
101
102     EnterCriticalSection(&pTransform->csReceive);
103     if (pTransform->filter.state == State_Stopped)
104     {
105         LeaveCriticalSection(&pTransform->csReceive);
106         return VFW_E_WRONG_STATE;
107     }
108
109     if (This->end_of_stream || This->flushing)
110     {
111         LeaveCriticalSection(&pTransform->csReceive);
112         return S_FALSE;
113     }
114
115     LeaveCriticalSection(&pTransform->csReceive);
116     if (pTransform->pFuncsTable->pfnReceive)
117         hr = pTransform->pFuncsTable->pfnReceive(pTransform, pInSample);
118     else
119         hr = S_FALSE;
120
121     return hr;
122 }
123
124 static HRESULT WINAPI TransformFilter_Output_QueryAccept(IPin *iface, const AM_MEDIA_TYPE * pmt)
125 {
126     BasePin *This = impl_BasePin_from_IPin(iface);
127     TransformFilter *pTransformFilter = impl_from_IBaseFilter(This->pinInfo.pFilter);
128     AM_MEDIA_TYPE* outpmt = &pTransformFilter->pmt;
129     TRACE("%p\n", iface);
130
131     if (IsEqualIID(&pmt->majortype, &outpmt->majortype)
132         && (IsEqualIID(&pmt->subtype, &outpmt->subtype) || IsEqualIID(&outpmt->subtype, &GUID_NULL)))
133         return S_OK;
134     return S_FALSE;
135 }
136
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     return pTransformFilter->pFuncsTable->pfnDecideBufferSize(pTransformFilter, pAlloc, ppropInputRequest);
141 }
142
143 static HRESULT WINAPI TransformFilter_Output_GetMediaType(BasePin *This, int iPosition, AM_MEDIA_TYPE *pmt)
144 {
145     TransformFilter *pTransform = impl_from_IBaseFilter(This->pinInfo.pFilter);
146
147     if (iPosition < 0)
148         return E_INVALIDARG;
149     if (iPosition > 0)
150         return VFW_S_NO_MORE_ITEMS;
151     CopyMediaType(pmt, &pTransform->pmt);
152     return S_OK;
153 }
154
155 static IPin* WINAPI TransformFilter_GetPin(BaseFilter *iface, int pos)
156 {
157     TransformFilter *This = impl_from_BaseFilter(iface);
158
159     if (pos >= This->npins || pos < 0)
160         return NULL;
161
162     IPin_AddRef(This->ppPins[pos]);
163     return This->ppPins[pos];
164 }
165
166 static LONG WINAPI TransformFilter_GetPinCount(BaseFilter *iface)
167 {
168     TransformFilter *This = impl_from_BaseFilter(iface);
169
170     return (This->npins+1);
171 }
172
173 static const BaseFilterFuncTable tfBaseFuncTable = {
174     TransformFilter_GetPin,
175     TransformFilter_GetPinCount
176 };
177
178 static const  BasePinFuncTable tf_input_BaseFuncTable = {
179     TransformFilter_Input_CheckMediaType,
180     NULL,
181     BasePinImpl_GetMediaTypeVersion,
182     BasePinImpl_GetMediaType
183 };
184
185 static const BaseInputPinFuncTable tf_input_BaseInputFuncTable = {
186     TransformFilter_Input_Receive
187 };
188
189 static const  BasePinFuncTable tf_output_BaseFuncTable = {
190     NULL,
191     BaseOutputPinImpl_AttemptConnection,
192     BasePinImpl_GetMediaTypeVersion,
193     TransformFilter_Output_GetMediaType
194 };
195
196 static const BaseOutputPinFuncTable tf_output_BaseOutputFuncTable = {
197     TransformFilter_Output_DecideBufferSize,
198     BaseOutputPinImpl_DecideAllocator,
199     BaseOutputPinImpl_BreakConnect
200 };
201
202 static HRESULT TransformFilter_Init(const IBaseFilterVtbl *pVtbl, const CLSID* pClsid, const TransformFilterFuncTable* pFuncsTable, TransformFilter* pTransformFilter)
203 {
204     HRESULT hr;
205     PIN_INFO piInput;
206     PIN_INFO piOutput;
207
208     BaseFilter_Init(&pTransformFilter->filter, pVtbl, pClsid, (DWORD_PTR)(__FILE__ ": TransformFilter.csFilter"), &tfBaseFuncTable);
209
210     InitializeCriticalSection(&pTransformFilter->csReceive);
211     pTransformFilter->csReceive.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__": TransformFilter.csReceive");
212
213     /* pTransformFilter is already allocated */
214     pTransformFilter->pFuncsTable = pFuncsTable;
215     ZeroMemory(&pTransformFilter->pmt, sizeof(pTransformFilter->pmt));
216     pTransformFilter->npins = 2;
217
218     pTransformFilter->ppPins = CoTaskMemAlloc(2 * sizeof(IPin *));
219
220     /* construct input pin */
221     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     if (SUCCEEDED(hr))
231     {
232         hr = BaseOutputPin_Construct(&TransformFilter_OutputPin_Vtbl, sizeof(BaseOutputPin), &piOutput, &tf_output_BaseFuncTable, &tf_output_BaseOutputFuncTable, &pTransformFilter->filter.csFilter, &pTransformFilter->ppPins[1]);
233
234         if (FAILED(hr))
235             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     return hr;
248 }
249
250 HRESULT TransformFilter_Construct(const IBaseFilterVtbl *pVtbl, LONG filter_size, const CLSID* pClsid, const TransformFilterFuncTable* pFuncsTable, IBaseFilter ** ppTransformFilter)
251 {
252     TransformFilter* pTf;
253
254     *ppTransformFilter = NULL;
255
256     assert(filter_size >= sizeof(TransformFilter));
257
258     pTf = CoTaskMemAlloc(filter_size);
259
260     if (!pTf)
261         return E_OUTOFMEMORY;
262
263     ZeroMemory(pTf, filter_size);
264
265     if (SUCCEEDED(TransformFilter_Init(pVtbl, pClsid, pFuncsTable, pTf)))
266     {
267         *ppTransformFilter = &pTf->filter.IBaseFilter_iface;
268         return S_OK;
269     }
270
271     CoTaskMemFree(pTf);
272     return E_FAIL;
273 }
274
275 HRESULT WINAPI TransformFilterImpl_QueryInterface(IBaseFilter * iface, REFIID riid, LPVOID * ppv)
276 {
277     HRESULT hr;
278     TransformFilter *This = impl_from_IBaseFilter(iface);
279     TRACE("(%p/%p)->(%s, %p)\n", This, iface, debugstr_guid(riid), ppv);
280
281     if (IsEqualIID(riid, &IID_IQualityControl))  {
282         *ppv = (IQualityControl*)This->qcimpl;
283         IUnknown_AddRef((IUnknown*)*ppv);
284         return S_OK;
285     }
286     hr = BaseFilterImpl_QueryInterface(iface, riid, ppv);
287
288     if (FAILED(hr) && !IsEqualIID(riid, &IID_IPin) && !IsEqualIID(riid, &IID_IVideoWindow) &&
289         !IsEqualIID(riid, &IID_IAMFilterMiscFlags))
290         FIXME("No interface for %s!\n", debugstr_guid(riid));
291
292     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     if (!refCount)
303     {
304         ULONG i;
305
306         for (i = 0; i < This->npins; i++)
307         {
308             IPin *pConnectedTo;
309
310             if (SUCCEEDED(IPin_ConnectedTo(This->ppPins[i], &pConnectedTo)))
311             {
312                 IPin_Disconnect(pConnectedTo);
313                 IPin_Release(pConnectedTo);
314             }
315             IPin_Disconnect(This->ppPins[i]);
316
317             IPin_Release(This->ppPins[i]);
318         }
319
320         CoTaskMemFree(This->ppPins);
321
322         TRACE("Destroying transform filter\n");
323         This->csReceive.DebugInfo->Spare[0] = 0;
324         DeleteCriticalSection(&This->csReceive);
325         FreeMediaType(&This->pmt);
326         QualityControlImpl_Destroy(This->qcimpl);
327         CoTaskMemFree(This);
328
329         return 0;
330     }
331     else
332         return refCount;
333 }
334
335 /** IMediaFilter methods **/
336
337 HRESULT WINAPI TransformFilterImpl_Stop(IBaseFilter * iface)
338 {
339     TransformFilter *This = impl_from_IBaseFilter(iface);
340     HRESULT hr = S_OK;
341
342     TRACE("(%p/%p)\n", This, iface);
343
344     EnterCriticalSection(&This->csReceive);
345     {
346         This->filter.state = State_Stopped;
347         if (This->pFuncsTable->pfnStopStreaming)
348             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     TRACE("(%p/%p)->()\n", This, iface);
361
362     EnterCriticalSection(&This->csReceive);
363     {
364         if (This->filter.state == State_Stopped)
365             hr = IBaseFilter_Run(iface, -1);
366         else
367             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     TRACE("(%p/%p)->(%s)\n", This, iface, wine_dbgstr_longlong(tStart));
383
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             if (This->pFuncsTable->pfnStartStreaming)
390                 hr = This->pFuncsTable->pfnStartStreaming(This);
391             if (SUCCEEDED(hr))
392                 hr = BaseOutputPinImpl_Active(impl_BaseOutputPin_from_IPin(This->ppPins[1]));
393         }
394
395         if (SUCCEEDED(hr))
396         {
397             This->filter.rtStreamStart = tStart;
398             This->filter.state = State_Running;
399         }
400     }
401     LeaveCriticalSection(&This->csReceive);
402
403     return hr;
404 }
405
406 HRESULT WINAPI TransformFilterImpl_Notify(TransformFilter *iface, IBaseFilter *sender, Quality qm)
407 {
408     return QualityControlImpl_Notify((IQualityControl*)iface->qcimpl, sender, qm);
409 }
410
411 /** IBaseFilter implementation **/
412
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     return E_NOTIMPL;
420 }
421
422 static const IBaseFilterVtbl TransformFilter_Vtbl =
423 {
424     TransformFilterImpl_QueryInterface,
425     BaseFilterImpl_AddRef,
426     TransformFilterImpl_Release,
427     BaseFilterImpl_GetClassID,
428     TransformFilterImpl_Stop,
429     TransformFilterImpl_Pause,
430     TransformFilterImpl_Run,
431     BaseFilterImpl_GetState,
432     BaseFilterImpl_SetSyncSource,
433     BaseFilterImpl_GetSyncSource,
434     BaseFilterImpl_EnumPins,
435     TransformFilterImpl_FindPin,
436     BaseFilterImpl_QueryFilterInfo,
437     BaseFilterImpl_JoinFilterGraph,
438     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     IPin* ppin;
446     HRESULT hr;
447
448     TRACE("(%p)->()\n", iface);
449
450     /* Since we process samples synchronously, just forward notification downstream */
451     pTransform = impl_from_IBaseFilter(This->pin.pinInfo.pFilter);
452     if (!pTransform)
453         hr = E_FAIL;
454     else
455         hr = IPin_ConnectedTo(pTransform->ppPins[1], &ppin);
456     if (SUCCEEDED(hr))
457     {
458         hr = IPin_EndOfStream(ppin);
459         IPin_Release(ppin);
460     }
461
462     if (FAILED(hr))
463         ERR("%x\n", hr);
464     return hr;
465 }
466
467 static HRESULT WINAPI TransformFilter_InputPin_ReceiveConnection(IPin * iface, IPin * pReceivePin, const AM_MEDIA_TYPE * pmt)
468 {
469     BaseInputPin* This = impl_BaseInputPin_from_IPin(iface);
470     TransformFilter* pTransform;
471     HRESULT hr = S_OK;
472
473     TRACE("(%p)->(%p, %p)\n", iface, pReceivePin, pmt);
474
475     pTransform = impl_from_IBaseFilter(This->pin.pinInfo.pFilter);
476
477     if (pTransform->pFuncsTable->pfnSetMediaType)
478         hr = pTransform->pFuncsTable->pfnSetMediaType(pTransform, PINDIR_INPUT, pmt);
479
480     if (SUCCEEDED(hr) && pTransform->pFuncsTable->pfnCompleteConnect)
481         hr = pTransform->pFuncsTable->pfnCompleteConnect(pTransform, PINDIR_INPUT, pReceivePin);
482
483     if (SUCCEEDED(hr))
484     {
485         hr = BaseInputPinImpl_ReceiveConnection(iface, pReceivePin, pmt);
486         if (FAILED(hr) && pTransform->pFuncsTable->pfnBreakConnect)
487             pTransform->pFuncsTable->pfnBreakConnect(pTransform, PINDIR_INPUT);
488     }
489
490     return hr;
491 }
492
493 static HRESULT WINAPI TransformFilter_InputPin_Disconnect(IPin * iface)
494 {
495     BaseInputPin* This = impl_BaseInputPin_from_IPin(iface);
496     TransformFilter* pTransform;
497
498     TRACE("(%p)->()\n", iface);
499
500     pTransform = impl_from_IBaseFilter(This->pin.pinInfo.pFilter);
501     if (pTransform->pFuncsTable->pfnBreakConnect)
502         pTransform->pFuncsTable->pfnBreakConnect(pTransform, PINDIR_INPUT);
503
504     return BasePinImpl_Disconnect(iface);
505 }
506
507 static HRESULT WINAPI TransformFilter_InputPin_BeginFlush(IPin * iface)
508 {
509     BaseInputPin* This = impl_BaseInputPin_from_IPin(iface);
510     TransformFilter* pTransform;
511     HRESULT hr = S_OK;
512
513     TRACE("(%p)->()\n", iface);
514
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     return hr;
523 }
524
525 static HRESULT WINAPI TransformFilter_InputPin_EndFlush(IPin * iface)
526 {
527     BaseInputPin* This = impl_BaseInputPin_from_IPin(iface);
528     TransformFilter* pTransform;
529     HRESULT hr = S_OK;
530
531     TRACE("(%p)->()\n", iface);
532
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     return hr;
541 }
542
543 static HRESULT WINAPI TransformFilter_InputPin_NewSegment(IPin * iface, REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate)
544 {
545     BaseInputPin* This = impl_BaseInputPin_from_IPin(iface);
546     TransformFilter* pTransform;
547     HRESULT hr = S_OK;
548
549     TRACE("(%p)->()\n", iface);
550
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     return hr;
559 }
560
561 static const IPinVtbl TransformFilter_InputPin_Vtbl =
562 {
563     BaseInputPinImpl_QueryInterface,
564     BasePinImpl_AddRef,
565     BaseInputPinImpl_Release,
566     BaseInputPinImpl_Connect,
567     TransformFilter_InputPin_ReceiveConnection,
568     TransformFilter_InputPin_Disconnect,
569     BasePinImpl_ConnectedTo,
570     BasePinImpl_ConnectionMediaType,
571     BasePinImpl_QueryPinInfo,
572     BasePinImpl_QueryDirection,
573     BasePinImpl_QueryId,
574     BaseInputPinImpl_QueryAccept,
575     BasePinImpl_EnumMediaTypes,
576     BasePinImpl_QueryInternalConnections,
577     TransformFilter_InputPin_EndOfStream,
578     TransformFilter_InputPin_BeginFlush,
579     TransformFilter_InputPin_EndFlush,
580     TransformFilter_InputPin_NewSegment
581 };
582
583 static const IPinVtbl TransformFilter_OutputPin_Vtbl =
584 {
585     BaseOutputPinImpl_QueryInterface,
586     BasePinImpl_AddRef,
587     BaseOutputPinImpl_Release,
588     BaseOutputPinImpl_Connect,
589     BaseOutputPinImpl_ReceiveConnection,
590     BaseOutputPinImpl_Disconnect,
591     BasePinImpl_ConnectedTo,
592     BasePinImpl_ConnectionMediaType,
593     BasePinImpl_QueryPinInfo,
594     BasePinImpl_QueryDirection,
595     BasePinImpl_QueryId,
596     TransformFilter_Output_QueryAccept,
597     BasePinImpl_EnumMediaTypes,
598     BasePinImpl_QueryInternalConnections,
599     BaseOutputPinImpl_EndOfStream,
600     BaseOutputPinImpl_BeginFlush,
601     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     if (This->pFuncsTable->pfnNotify)
610         return This->pFuncsTable->pfnNotify(This, sender, qm);
611     else
612         return TransformFilterImpl_Notify(This, sender, qm);
613 }
614
615 static const IQualityControlVtbl TransformFilter_QualityControl_Vtbl = {
616     QualityControlImpl_QueryInterface,
617     QualityControlImpl_AddRef,
618     QualityControlImpl_Release,
619     TransformFilter_QualityControlImpl_Notify,
620     QualityControlImpl_SetSink
621 };