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 IPinVtbl TransformFilter_InputPin_Vtbl; 46 static const IPinVtbl TransformFilter_OutputPin_Vtbl; 47 static const IQualityControlVtbl TransformFilter_QualityControl_Vtbl; 48 49 static inline BaseInputPin *impl_BaseInputPin_from_BasePin( BasePin *iface ) 50 { 51 return CONTAINING_RECORD(iface, BaseInputPin, pin); 52 } 53 54 static inline BasePin *impl_BasePin_from_IPin( IPin *iface ) 55 { 56 return CONTAINING_RECORD(iface, BasePin, IPin_iface); 57 } 58 59 static inline BaseInputPin *impl_BaseInputPin_from_IPin( IPin *iface ) 60 { 61 return CONTAINING_RECORD(iface, BaseInputPin, pin.IPin_iface); 62 } 63 64 static inline BaseOutputPin *impl_BaseOutputPin_from_IPin( IPin *iface ) 65 { 66 return CONTAINING_RECORD(iface, BaseOutputPin, pin.IPin_iface); 67 } 68 69 static inline TransformFilter *impl_from_IBaseFilter( IBaseFilter *iface ) 70 { 71 return CONTAINING_RECORD(iface, TransformFilter, filter.IBaseFilter_iface); 72 } 73 74 static inline TransformFilter *impl_from_BaseFilter( BaseFilter *iface ) 75 { 76 return CONTAINING_RECORD(iface, TransformFilter, filter); 77 } 78 79 static HRESULT WINAPI TransformFilter_Input_CheckMediaType(BasePin *iface, const AM_MEDIA_TYPE * pmt) 80 { 81 BaseInputPin* This = impl_BaseInputPin_from_BasePin(iface); 82 TransformFilter * pTransform; 83 84 TRACE("%p\n", iface); 85 pTransform = impl_from_IBaseFilter(This->pin.pinInfo.pFilter); 86 87 if (pTransform->pFuncsTable->pfnCheckInputType) 88 return pTransform->pFuncsTable->pfnCheckInputType(pTransform, pmt); 89 /* Assume OK if there's no query method (the connection will fail if 90 needed) */ 91 return S_OK; 92 } 93 94 static HRESULT WINAPI TransformFilter_Input_Receive(BaseInputPin *This, IMediaSample *pInSample) 95 { 96 HRESULT hr; 97 TransformFilter * pTransform; 98 TRACE("%p\n", This); 99 pTransform = impl_from_IBaseFilter(This->pin.pinInfo.pFilter); 100 101 EnterCriticalSection(&pTransform->csReceive); 102 if (pTransform->filter.state == State_Stopped) 103 { 104 LeaveCriticalSection(&pTransform->csReceive); 105 return VFW_E_WRONG_STATE; 106 } 107 108 if (This->end_of_stream || This->flushing) 109 { 110 LeaveCriticalSection(&pTransform->csReceive); 111 return S_FALSE; 112 } 113 114 LeaveCriticalSection(&pTransform->csReceive); 115 if (pTransform->pFuncsTable->pfnReceive) 116 hr = pTransform->pFuncsTable->pfnReceive(pTransform, pInSample); 117 else 118 hr = S_FALSE; 119 120 return hr; 121 } 122 123 static HRESULT WINAPI TransformFilter_Output_QueryAccept(IPin *iface, const AM_MEDIA_TYPE * pmt) 124 { 125 BasePin *This = impl_BasePin_from_IPin(iface); 126 TransformFilter *pTransformFilter = impl_from_IBaseFilter(This->pinInfo.pFilter); 127 AM_MEDIA_TYPE* outpmt = &pTransformFilter->pmt; 128 TRACE("%p\n", iface); 129 130 if (IsEqualIID(&pmt->majortype, &outpmt->majortype) 131 && (IsEqualIID(&pmt->subtype, &outpmt->subtype) || IsEqualIID(&outpmt->subtype, &GUID_NULL))) 132 return S_OK; 133 return S_FALSE; 134 } 135 136 static HRESULT WINAPI TransformFilter_Output_DecideBufferSize(BaseOutputPin *This, IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *ppropInputRequest) 137 { 138 TransformFilter *pTransformFilter = impl_from_IBaseFilter(This->pin.pinInfo.pFilter); 139 return pTransformFilter->pFuncsTable->pfnDecideBufferSize(pTransformFilter, pAlloc, ppropInputRequest); 140 } 141 142 static HRESULT WINAPI TransformFilter_Output_GetMediaType(BasePin *This, int iPosition, AM_MEDIA_TYPE *pmt) 143 { 144 TransformFilter *pTransform = impl_from_IBaseFilter(This->pinInfo.pFilter); 145 146 if (iPosition < 0) 147 return E_INVALIDARG; 148 if (iPosition > 0) 149 return VFW_S_NO_MORE_ITEMS; 150 CopyMediaType(pmt, &pTransform->pmt); 151 return S_OK; 152 } 153 154 static IPin* WINAPI TransformFilter_GetPin(BaseFilter *iface, int pos) 155 { 156 TransformFilter *This = impl_from_BaseFilter(iface); 157 158 if (pos >= This->npins || pos < 0) 159 return NULL; 160 161 IPin_AddRef(This->ppPins[pos]); 162 return This->ppPins[pos]; 163 } 164 165 static LONG WINAPI TransformFilter_GetPinCount(BaseFilter *iface) 166 { 167 TransformFilter *This = impl_from_BaseFilter(iface); 168 169 return (This->npins+1); 170 } 171 172 static const BaseFilterFuncTable tfBaseFuncTable = { 173 TransformFilter_GetPin, 174 TransformFilter_GetPinCount 175 }; 176 177 static const BaseInputPinFuncTable tf_input_BaseInputFuncTable = { 178 { 179 TransformFilter_Input_CheckMediaType, 180 NULL, 181 BasePinImpl_GetMediaTypeVersion, 182 BasePinImpl_GetMediaType 183 }, 184 TransformFilter_Input_Receive 185 }; 186 187 static const BaseOutputPinFuncTable tf_output_BaseOutputFuncTable = { 188 { 189 NULL, 190 BaseOutputPinImpl_AttemptConnection, 191 BasePinImpl_GetMediaTypeVersion, 192 TransformFilter_Output_GetMediaType 193 }, 194 TransformFilter_Output_DecideBufferSize, 195 BaseOutputPinImpl_DecideAllocator, 196 BaseOutputPinImpl_BreakConnect 197 }; 198 199 static HRESULT TransformFilter_Init(const IBaseFilterVtbl *pVtbl, const CLSID* pClsid, const TransformFilterFuncTable* pFuncsTable, TransformFilter* pTransformFilter) 200 { 201 HRESULT hr; 202 PIN_INFO piInput; 203 PIN_INFO piOutput; 204 205 BaseFilter_Init(&pTransformFilter->filter, pVtbl, pClsid, (DWORD_PTR)(__FILE__ ": TransformFilter.csFilter"), &tfBaseFuncTable); 206 207 InitializeCriticalSection(&pTransformFilter->csReceive); 208 pTransformFilter->csReceive.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__": TransformFilter.csReceive"); 209 210 /* pTransformFilter is already allocated */ 211 pTransformFilter->pFuncsTable = pFuncsTable; 212 ZeroMemory(&pTransformFilter->pmt, sizeof(pTransformFilter->pmt)); 213 pTransformFilter->npins = 2; 214 215 pTransformFilter->ppPins = CoTaskMemAlloc(2 * sizeof(IPin *)); 216 217 /* construct input pin */ 218 piInput.dir = PINDIR_INPUT; 219 piInput.pFilter = &pTransformFilter->filter.IBaseFilter_iface; 220 lstrcpynW(piInput.achName, wcsInputPinName, sizeof(piInput.achName) / sizeof(piInput.achName[0])); 221 piOutput.dir = PINDIR_OUTPUT; 222 piOutput.pFilter = &pTransformFilter->filter.IBaseFilter_iface; 223 lstrcpynW(piOutput.achName, wcsOutputPinName, sizeof(piOutput.achName) / sizeof(piOutput.achName[0])); 224 225 hr = BaseInputPin_Construct(&TransformFilter_InputPin_Vtbl, sizeof(BaseInputPin), &piInput, 226 &tf_input_BaseInputFuncTable, &pTransformFilter->filter.csFilter, NULL, &pTransformFilter->ppPins[0]); 227 228 if (SUCCEEDED(hr)) 229 { 230 hr = BaseOutputPin_Construct(&TransformFilter_OutputPin_Vtbl, sizeof(BaseOutputPin), &piOutput, &tf_output_BaseOutputFuncTable, &pTransformFilter->filter.csFilter, &pTransformFilter->ppPins[1]); 231 232 if (FAILED(hr)) 233 ERR("Cannot create output pin (%x)\n", hr); 234 else { 235 QualityControlImpl_Create( pTransformFilter->ppPins[0], &pTransformFilter->filter.IBaseFilter_iface, &pTransformFilter->qcimpl); 236 pTransformFilter->qcimpl->IQualityControl_iface.lpVtbl = &TransformFilter_QualityControl_Vtbl; 237 } 238 } 239 240 if (SUCCEEDED(hr)) 241 { 242 ISeekingPassThru *passthru; 243 pTransformFilter->seekthru_unk = NULL; 244 hr = CoCreateInstance(&CLSID_SeekingPassThru, (IUnknown *)&pTransformFilter->filter.IBaseFilter_iface, CLSCTX_INPROC_SERVER, 245 &IID_IUnknown, (void **)&pTransformFilter->seekthru_unk); 246 if (SUCCEEDED(hr)) 247 { 248 IUnknown_QueryInterface(pTransformFilter->seekthru_unk, &IID_ISeekingPassThru, (void**)&passthru); 249 ISeekingPassThru_Init(passthru, FALSE, pTransformFilter->ppPins[0]); 250 ISeekingPassThru_Release(passthru); 251 } 252 } 253 254 if (FAILED(hr)) 255 { 256 CoTaskMemFree(pTransformFilter->ppPins); 257 BaseFilterImpl_Release(&pTransformFilter->filter.IBaseFilter_iface); 258 } 259 260 return hr; 261 } 262 263 HRESULT TransformFilter_Construct(const IBaseFilterVtbl *pVtbl, LONG filter_size, const CLSID* pClsid, const TransformFilterFuncTable* pFuncsTable, IBaseFilter ** ppTransformFilter) 264 { 265 TransformFilter* pTf; 266 267 *ppTransformFilter = NULL; 268 269 assert(filter_size >= sizeof(TransformFilter)); 270 271 pTf = CoTaskMemAlloc(filter_size); 272 273 if (!pTf) 274 return E_OUTOFMEMORY; 275 276 ZeroMemory(pTf, filter_size); 277 278 if (SUCCEEDED(TransformFilter_Init(pVtbl, pClsid, pFuncsTable, pTf))) 279 { 280 *ppTransformFilter = &pTf->filter.IBaseFilter_iface; 281 return S_OK; 282 } 283 284 CoTaskMemFree(pTf); 285 return E_FAIL; 286 } 287 288 HRESULT WINAPI TransformFilterImpl_QueryInterface(IBaseFilter * iface, REFIID riid, LPVOID * ppv) 289 { 290 HRESULT hr; 291 TransformFilter *This = impl_from_IBaseFilter(iface); 292 TRACE("(%p/%p)->(%s, %p)\n", This, iface, debugstr_guid(riid), ppv); 293 294 if (IsEqualIID(riid, &IID_IQualityControl)) { 295 *ppv = (IQualityControl*)This->qcimpl; 296 IUnknown_AddRef((IUnknown*)*ppv); 297 return S_OK; 298 } 299 else if (IsEqualIID(riid, &IID_IMediaSeeking) || 300 IsEqualIID(riid, &IID_IMediaPosition)) 301 { 302 return IUnknown_QueryInterface(This->seekthru_unk, riid, ppv); 303 } 304 hr = BaseFilterImpl_QueryInterface(iface, riid, ppv); 305 306 if (FAILED(hr) && !IsEqualIID(riid, &IID_IPin) && !IsEqualIID(riid, &IID_IVideoWindow) && 307 !IsEqualIID(riid, &IID_IAMFilterMiscFlags)) 308 FIXME("No interface for %s!\n", debugstr_guid(riid)); 309 310 return hr; 311 } 312 313 ULONG WINAPI TransformFilterImpl_Release(IBaseFilter * iface) 314 { 315 TransformFilter *This = impl_from_IBaseFilter(iface); 316 ULONG refCount = InterlockedDecrement(&This->filter.refCount); 317 318 TRACE("(%p/%p)->() Release from %d\n", This, iface, refCount + 1); 319 320 if (!refCount) 321 { 322 ULONG i; 323 324 for (i = 0; i < This->npins; i++) 325 { 326 IPin *pConnectedTo; 327 328 if (SUCCEEDED(IPin_ConnectedTo(This->ppPins[i], &pConnectedTo))) 329 { 330 IPin_Disconnect(pConnectedTo); 331 IPin_Release(pConnectedTo); 332 } 333 IPin_Disconnect(This->ppPins[i]); 334 335 IPin_Release(This->ppPins[i]); 336 } 337 338 CoTaskMemFree(This->ppPins); 339 340 TRACE("Destroying transform filter\n"); 341 This->csReceive.DebugInfo->Spare[0] = 0; 342 DeleteCriticalSection(&This->csReceive); 343 FreeMediaType(&This->pmt); 344 QualityControlImpl_Destroy(This->qcimpl); 345 IUnknown_Release(This->seekthru_unk); 346 BaseFilter_Destroy(&This->filter); 347 CoTaskMemFree(This); 348 } 349 return refCount; 350 } 351 352 /** IMediaFilter methods **/ 353 354 HRESULT WINAPI TransformFilterImpl_Stop(IBaseFilter * iface) 355 { 356 TransformFilter *This = impl_from_IBaseFilter(iface); 357 HRESULT hr = S_OK; 358 359 TRACE("(%p/%p)\n", This, iface); 360 361 EnterCriticalSection(&This->csReceive); 362 { 363 This->filter.state = State_Stopped; 364 if (This->pFuncsTable->pfnStopStreaming) 365 hr = This->pFuncsTable->pfnStopStreaming(This); 366 if (SUCCEEDED(hr)) 367 hr = BaseOutputPinImpl_Inactive(impl_BaseOutputPin_from_IPin(This->ppPins[1])); 368 } 369 LeaveCriticalSection(&This->csReceive); 370 371 return hr; 372 } 373 374 HRESULT WINAPI TransformFilterImpl_Pause(IBaseFilter * iface) 375 { 376 TransformFilter *This = impl_from_IBaseFilter(iface); 377 HRESULT hr; 378 379 TRACE("(%p/%p)->()\n", This, iface); 380 381 EnterCriticalSection(&This->csReceive); 382 { 383 if (This->filter.state == State_Stopped) 384 hr = IBaseFilter_Run(iface, -1); 385 else 386 hr = S_OK; 387 388 if (SUCCEEDED(hr)) 389 This->filter.state = State_Paused; 390 } 391 LeaveCriticalSection(&This->csReceive); 392 393 return hr; 394 } 395 396 HRESULT WINAPI TransformFilterImpl_Run(IBaseFilter * iface, REFERENCE_TIME tStart) 397 { 398 HRESULT hr = S_OK; 399 TransformFilter *This = impl_from_IBaseFilter(iface); 400 401 TRACE("(%p/%p)->(%s)\n", This, iface, wine_dbgstr_longlong(tStart)); 402 403 EnterCriticalSection(&This->csReceive); 404 { 405 if (This->filter.state == State_Stopped) 406 { 407 impl_BaseInputPin_from_IPin(This->ppPins[0])->end_of_stream = FALSE; 408 if (This->pFuncsTable->pfnStartStreaming) 409 hr = This->pFuncsTable->pfnStartStreaming(This); 410 if (SUCCEEDED(hr)) 411 hr = BaseOutputPinImpl_Active(impl_BaseOutputPin_from_IPin(This->ppPins[1])); 412 } 413 414 if (SUCCEEDED(hr)) 415 { 416 This->filter.rtStreamStart = tStart; 417 This->filter.state = State_Running; 418 } 419 } 420 LeaveCriticalSection(&This->csReceive); 421 422 return hr; 423 } 424 425 HRESULT WINAPI TransformFilterImpl_Notify(TransformFilter *iface, IBaseFilter *sender, Quality qm) 426 { 427 return QualityControlImpl_Notify((IQualityControl*)iface->qcimpl, sender, qm); 428 } 429 430 /** IBaseFilter implementation **/ 431 432 HRESULT WINAPI TransformFilterImpl_FindPin(IBaseFilter * iface, LPCWSTR Id, IPin **ppPin) 433 { 434 TransformFilter *This = impl_from_IBaseFilter(iface); 435 436 TRACE("(%p/%p)->(%s,%p)\n", This, iface, debugstr_w(Id), ppPin); 437 438 return E_NOTIMPL; 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)->(%s %s %e)\n", iface, wine_dbgstr_longlong(tStart), wine_dbgstr_longlong(tStop), dRate); 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 }; 622