1 /* 2 * Generic Implementation of IPin Interface 3 * 4 * Copyright 2003 Robert Shearman 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 22 #define COBJMACROS 23 24 #include "dshow.h" 25 #include "wine/debug.h" 26 #include "wine/unicode.h" 27 #include "wine/strmbase.h" 28 #include "uuids.h" 29 #include "vfwmsgs.h" 30 #include <assert.h> 31 32 WINE_DEFAULT_DEBUG_CHANNEL(strmbase); 33 34 static const IMemInputPinVtbl MemInputPin_Vtbl; 35 36 typedef HRESULT (*SendPinFunc)( IPin *to, LPVOID arg ); 37 38 static inline BasePin *impl_from_IPin( IPin *iface ) 39 { 40 return CONTAINING_RECORD(iface, BasePin, IPin_iface); 41 } 42 43 /** Helper function, there are a lot of places where the error code is inherited 44 * The following rules apply: 45 * 46 * Return the first received error code (E_NOTIMPL is ignored) 47 * If no errors occur: return the first received non-error-code that isn't S_OK 48 */ 49 static HRESULT updatehres( HRESULT original, HRESULT new ) 50 { 51 if (FAILED( original ) || new == E_NOTIMPL) 52 return original; 53 54 if (FAILED( new ) || original == S_OK) 55 return new; 56 57 return original; 58 } 59 60 /** Sends a message from a pin further to other, similar pins 61 * fnMiddle is called on each pin found further on the stream. 62 * fnEnd (can be NULL) is called when the message can't be sent any further (this is a renderer or source) 63 * 64 * If the pin given is an input pin, the message will be sent downstream to other input pins 65 * If the pin given is an output pin, the message will be sent upstream to other output pins 66 */ 67 static HRESULT SendFurther( IPin *from, SendPinFunc fnMiddle, LPVOID arg, SendPinFunc fnEnd ) 68 { 69 PIN_INFO pin_info; 70 ULONG amount = 0; 71 HRESULT hr = S_OK; 72 HRESULT hr_return = S_OK; 73 IEnumPins *enumpins = NULL; 74 BOOL foundend = TRUE; 75 PIN_DIRECTION from_dir; 76 77 IPin_QueryDirection( from, &from_dir ); 78 79 hr = IPin_QueryInternalConnections( from, NULL, &amount ); 80 if (hr != E_NOTIMPL && amount) 81 FIXME("Use QueryInternalConnections!\n"); 82 83 pin_info.pFilter = NULL; 84 hr = IPin_QueryPinInfo( from, &pin_info ); 85 if (FAILED(hr)) 86 goto out; 87 88 hr = IBaseFilter_EnumPins( pin_info.pFilter, &enumpins ); 89 if (FAILED(hr)) 90 goto out; 91 92 hr = IEnumPins_Reset( enumpins ); 93 while (hr == S_OK) { 94 IPin *pin = NULL; 95 hr = IEnumPins_Next( enumpins, 1, &pin, NULL ); 96 if (hr == VFW_E_ENUM_OUT_OF_SYNC) 97 { 98 hr = IEnumPins_Reset( enumpins ); 99 continue; 100 } 101 if (pin) 102 { 103 PIN_DIRECTION dir; 104 105 IPin_QueryDirection( pin, &dir ); 106 if (dir != from_dir) 107 { 108 IPin *connected = NULL; 109 110 foundend = FALSE; 111 IPin_ConnectedTo( pin, &connected ); 112 if (connected) 113 { 114 HRESULT hr_local; 115 116 hr_local = fnMiddle( connected, arg ); 117 hr_return = updatehres( hr_return, hr_local ); 118 IPin_Release(connected); 119 } 120 } 121 IPin_Release( pin ); 122 } 123 else 124 { 125 hr = S_OK; 126 break; 127 } 128 } 129 130 if (!foundend) 131 hr = hr_return; 132 else if (fnEnd) { 133 HRESULT hr_local; 134 135 hr_local = fnEnd( from, arg ); 136 hr_return = updatehres( hr_return, hr_local ); 137 } 138 IEnumPins_Release(enumpins); 139 140 out: 141 if (pin_info.pFilter) 142 IBaseFilter_Release( pin_info.pFilter ); 143 return hr; 144 } 145 146 static void Copy_PinInfo(PIN_INFO * pDest, const PIN_INFO * pSrc) 147 { 148 /* Tempting to just do a memcpy, but the name field is 149 128 characters long! We will probably never exceed 10 150 most of the time, so we are better off copying 151 each field manually */ 152 strcpyW(pDest->achName, pSrc->achName); 153 pDest->dir = pSrc->dir; 154 pDest->pFilter = pSrc->pFilter; 155 } 156 157 static void dump_AM_MEDIA_TYPE(const AM_MEDIA_TYPE * pmt) 158 { 159 if (!pmt) 160 return; 161 TRACE("\t%s\n\t%s\n\t...\n\t%s\n", debugstr_guid(&pmt->majortype), debugstr_guid(&pmt->subtype), debugstr_guid(&pmt->formattype)); 162 } 163 164 static BOOL CompareMediaTypes(const AM_MEDIA_TYPE * pmt1, const AM_MEDIA_TYPE * pmt2, BOOL bWildcards) 165 { 166 TRACE("pmt1: "); 167 dump_AM_MEDIA_TYPE(pmt1); 168 TRACE("pmt2: "); 169 dump_AM_MEDIA_TYPE(pmt2); 170 return (((bWildcards && (IsEqualGUID(&pmt1->majortype, &GUID_NULL) || IsEqualGUID(&pmt2->majortype, &GUID_NULL))) || IsEqualGUID(&pmt1->majortype, &pmt2->majortype)) && 171 ((bWildcards && (IsEqualGUID(&pmt1->subtype, &GUID_NULL) || IsEqualGUID(&pmt2->subtype, &GUID_NULL))) || IsEqualGUID(&pmt1->subtype, &pmt2->subtype))); 172 } 173 174 /*** Common Base Pin function */ 175 HRESULT WINAPI BasePinImpl_GetMediaType(BasePin *iface, int iPosition, AM_MEDIA_TYPE *pmt) 176 { 177 if (iPosition < 0) 178 return E_INVALIDARG; 179 return VFW_S_NO_MORE_ITEMS; 180 } 181 182 LONG WINAPI BasePinImpl_GetMediaTypeVersion(BasePin *iface) 183 { 184 return 1; 185 } 186 187 ULONG WINAPI BasePinImpl_AddRef(IPin * iface) 188 { 189 BasePin *This = impl_from_IPin(iface); 190 ULONG refCount = InterlockedIncrement(&This->refCount); 191 192 TRACE("(%p)->() AddRef from %d\n", iface, refCount - 1); 193 194 return refCount; 195 } 196 197 HRESULT WINAPI BasePinImpl_Disconnect(IPin * iface) 198 { 199 HRESULT hr; 200 BasePin *This = impl_from_IPin(iface); 201 202 TRACE("()\n"); 203 204 EnterCriticalSection(This->pCritSec); 205 { 206 if (This->pConnectedTo) 207 { 208 IPin_Release(This->pConnectedTo); 209 This->pConnectedTo = NULL; 210 FreeMediaType(&This->mtCurrent); 211 ZeroMemory(&This->mtCurrent, sizeof(This->mtCurrent)); 212 hr = S_OK; 213 } 214 else 215 hr = S_FALSE; 216 } 217 LeaveCriticalSection(This->pCritSec); 218 219 return hr; 220 } 221 222 HRESULT WINAPI BasePinImpl_ConnectedTo(IPin * iface, IPin ** ppPin) 223 { 224 HRESULT hr; 225 BasePin *This = impl_from_IPin(iface); 226 227 TRACE("(%p)\n", ppPin); 228 229 EnterCriticalSection(This->pCritSec); 230 { 231 if (This->pConnectedTo) 232 { 233 *ppPin = This->pConnectedTo; 234 IPin_AddRef(*ppPin); 235 hr = S_OK; 236 } 237 else 238 { 239 hr = VFW_E_NOT_CONNECTED; 240 *ppPin = NULL; 241 } 242 } 243 LeaveCriticalSection(This->pCritSec); 244 245 return hr; 246 } 247 248 HRESULT WINAPI BasePinImpl_ConnectionMediaType(IPin * iface, AM_MEDIA_TYPE * pmt) 249 { 250 HRESULT hr; 251 BasePin *This = impl_from_IPin(iface); 252 253 TRACE("(%p/%p)->(%p)\n", This, iface, pmt); 254 255 EnterCriticalSection(This->pCritSec); 256 { 257 if (This->pConnectedTo) 258 { 259 CopyMediaType(pmt, &This->mtCurrent); 260 hr = S_OK; 261 } 262 else 263 { 264 ZeroMemory(pmt, sizeof(*pmt)); 265 hr = VFW_E_NOT_CONNECTED; 266 } 267 } 268 LeaveCriticalSection(This->pCritSec); 269 270 return hr; 271 } 272 273 HRESULT WINAPI BasePinImpl_QueryPinInfo(IPin * iface, PIN_INFO * pInfo) 274 { 275 BasePin *This = impl_from_IPin(iface); 276 277 TRACE("(%p/%p)->(%p)\n", This, iface, pInfo); 278 279 Copy_PinInfo(pInfo, &This->pinInfo); 280 IBaseFilter_AddRef(pInfo->pFilter); 281 282 return S_OK; 283 } 284 285 HRESULT WINAPI BasePinImpl_QueryDirection(IPin * iface, PIN_DIRECTION * pPinDir) 286 { 287 BasePin *This = impl_from_IPin(iface); 288 289 TRACE("(%p/%p)->(%p)\n", This, iface, pPinDir); 290 291 *pPinDir = This->pinInfo.dir; 292 293 return S_OK; 294 } 295 296 HRESULT WINAPI BasePinImpl_QueryId(IPin * iface, LPWSTR * Id) 297 { 298 BasePin *This = impl_from_IPin(iface); 299 300 TRACE("(%p/%p)->(%p)\n", This, iface, Id); 301 302 *Id = CoTaskMemAlloc((strlenW(This->pinInfo.achName) + 1) * sizeof(WCHAR)); 303 if (!*Id) 304 return E_OUTOFMEMORY; 305 306 strcpyW(*Id, This->pinInfo.achName); 307 308 return S_OK; 309 } 310 311 HRESULT WINAPI BasePinImpl_QueryAccept(IPin * iface, const AM_MEDIA_TYPE * pmt) 312 { 313 TRACE("(%p)->(%p)\n", iface, pmt); 314 315 return S_OK; 316 } 317 318 HRESULT WINAPI BasePinImpl_EnumMediaTypes(IPin * iface, IEnumMediaTypes ** ppEnum) 319 { 320 BasePin *This = impl_from_IPin(iface); 321 322 TRACE("(%p/%p)->(%p)\n", This, iface, ppEnum); 323 324 /* override this method to allow enumeration of your types */ 325 326 return EnumMediaTypes_Construct(This, This->pFuncsTable->pfnGetMediaType, This->pFuncsTable->pfnGetMediaTypeVersion , ppEnum); 327 } 328 329 HRESULT WINAPI BasePinImpl_QueryInternalConnections(IPin * iface, IPin ** apPin, ULONG * cPin) 330 { 331 BasePin *This = impl_from_IPin(iface); 332 333 TRACE("(%p/%p)->(%p, %p)\n", This, iface, apPin, cPin); 334 335 return E_NOTIMPL; /* to tell caller that all input pins connected to all output pins */ 336 } 337 338 HRESULT WINAPI BasePinImpl_NewSegment(IPin * iface, REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate) 339 { 340 BasePin *This = impl_from_IPin(iface); 341 342 TRACE("(%s, %s, %e)\n", wine_dbgstr_longlong(tStart), wine_dbgstr_longlong(tStop), dRate); 343 344 This->tStart = tStart; 345 This->tStop = tStop; 346 This->dRate = dRate; 347 348 return S_OK; 349 } 350 351 /*** OutputPin implementation ***/ 352 353 static inline BaseOutputPin *impl_BaseOutputPin_from_IPin( IPin *iface ) 354 { 355 return CONTAINING_RECORD(iface, BaseOutputPin, pin.IPin_iface); 356 } 357 358 static inline BaseOutputPin *impl_BaseOutputPin_from_BasePin( BasePin *iface ) 359 { 360 return CONTAINING_RECORD(iface, BaseOutputPin, pin); 361 } 362 363 HRESULT WINAPI BaseOutputPinImpl_QueryInterface(IPin * iface, REFIID riid, LPVOID * ppv) 364 { 365 BaseOutputPin *This = impl_BaseOutputPin_from_IPin(iface); 366 367 TRACE("(%p/%p)->(%s, %p)\n", This, iface, debugstr_guid(riid), ppv); 368 369 *ppv = NULL; 370 371 if (IsEqualIID(riid, &IID_IUnknown)) 372 *ppv = iface; 373 else if (IsEqualIID(riid, &IID_IPin)) 374 *ppv = iface; 375 else if (IsEqualIID(riid, &IID_IMediaSeeking) || 376 IsEqualIID(riid, &IID_IQualityControl)) 377 { 378 return IBaseFilter_QueryInterface(This->pin.pinInfo.pFilter, riid, ppv); 379 } 380 381 if (*ppv) 382 { 383 IUnknown_AddRef((IUnknown *)(*ppv)); 384 return S_OK; 385 } 386 387 FIXME("No interface for %s!\n", debugstr_guid(riid)); 388 389 return E_NOINTERFACE; 390 } 391 392 ULONG WINAPI BaseOutputPinImpl_Release(IPin * iface) 393 { 394 BaseOutputPin *This = impl_BaseOutputPin_from_IPin(iface); 395 ULONG refCount = InterlockedDecrement(&This->pin.refCount); 396 397 TRACE("(%p)->() Release from %d\n", iface, refCount + 1); 398 399 if (!refCount) 400 BaseOutputPin_Destroy(This); 401 402 return refCount; 403 } 404 405 HRESULT WINAPI BaseOutputPinImpl_Connect(IPin * iface, IPin * pReceivePin, const AM_MEDIA_TYPE * pmt) 406 { 407 HRESULT hr; 408 BaseOutputPin *This = impl_BaseOutputPin_from_IPin(iface); 409 410 TRACE("(%p/%p)->(%p, %p)\n", This, iface, pReceivePin, pmt); 411 dump_AM_MEDIA_TYPE(pmt); 412 413 if (!pReceivePin) 414 return E_POINTER; 415 416 /* If we try to connect to ourselves, we will definitely deadlock. 417 * There are other cases where we could deadlock too, but this 418 * catches the obvious case */ 419 assert(pReceivePin != iface); 420 421 EnterCriticalSection(This->pin.pCritSec); 422 { 423 /* if we have been a specific type to connect with, then we can either connect 424 * with that or fail. We cannot choose different AM_MEDIA_TYPE */ 425 if (pmt && !IsEqualGUID(&pmt->majortype, &GUID_NULL) && !IsEqualGUID(&pmt->subtype, &GUID_NULL)) 426 hr = This->pin.pFuncsTable->pfnAttemptConnection(&This->pin, pReceivePin, pmt); 427 else 428 { 429 /* negotiate media type */ 430 431 IEnumMediaTypes * pEnumCandidates; 432 AM_MEDIA_TYPE * pmtCandidate = NULL; /* Candidate media type */ 433 434 if (SUCCEEDED(hr = IPin_EnumMediaTypes(iface, &pEnumCandidates))) 435 { 436 hr = VFW_E_NO_ACCEPTABLE_TYPES; /* Assume the worst, but set to S_OK if connected successfully */ 437 438 /* try this filter's media types first */ 439 while (S_OK == IEnumMediaTypes_Next(pEnumCandidates, 1, &pmtCandidate, NULL)) 440 { 441 assert(pmtCandidate); 442 dump_AM_MEDIA_TYPE(pmtCandidate); 443 if (!IsEqualGUID(&FORMAT_None, &pmtCandidate->formattype) 444 && !IsEqualGUID(&GUID_NULL, &pmtCandidate->formattype)) 445 assert(pmtCandidate->pbFormat); 446 if (( !pmt || CompareMediaTypes(pmt, pmtCandidate, TRUE) ) && 447 (This->pin.pFuncsTable->pfnAttemptConnection(&This->pin, pReceivePin, pmtCandidate) == S_OK)) 448 { 449 hr = S_OK; 450 DeleteMediaType(pmtCandidate); 451 break; 452 } 453 DeleteMediaType(pmtCandidate); 454 pmtCandidate = NULL; 455 } 456 IEnumMediaTypes_Release(pEnumCandidates); 457 } 458 459 /* then try receiver filter's media types */ 460 if (hr != S_OK && SUCCEEDED(hr = IPin_EnumMediaTypes(pReceivePin, &pEnumCandidates))) /* if we haven't already connected successfully */ 461 { 462 ULONG fetched; 463 464 hr = VFW_E_NO_ACCEPTABLE_TYPES; /* Assume the worst, but set to S_OK if connected successfully */ 465 466 while (S_OK == IEnumMediaTypes_Next(pEnumCandidates, 1, &pmtCandidate, &fetched)) 467 { 468 assert(pmtCandidate); 469 dump_AM_MEDIA_TYPE(pmtCandidate); 470 if (( !pmt || CompareMediaTypes(pmt, pmtCandidate, TRUE) ) && 471 (This->pin.pFuncsTable->pfnAttemptConnection(&This->pin, pReceivePin, pmtCandidate) == S_OK)) 472 { 473 hr = S_OK; 474 DeleteMediaType(pmtCandidate); 475 break; 476 } 477 DeleteMediaType(pmtCandidate); 478 pmtCandidate = NULL; 479 } /* while */ 480 IEnumMediaTypes_Release(pEnumCandidates); 481 } /* if not found */ 482 } /* if negotiate media type */ 483 } /* if succeeded */ 484 LeaveCriticalSection(This->pin.pCritSec); 485 486 TRACE(" -- %x\n", hr); 487 return hr; 488 } 489 490 HRESULT WINAPI BaseOutputPinImpl_ReceiveConnection(IPin * iface, IPin * pReceivePin, const AM_MEDIA_TYPE * pmt) 491 { 492 ERR("Incoming connection on an output pin! (%p, %p)\n", pReceivePin, pmt); 493 494 return E_UNEXPECTED; 495 } 496 497 HRESULT WINAPI BaseOutputPinImpl_Disconnect(IPin * iface) 498 { 499 HRESULT hr; 500 BaseOutputPin *This = impl_BaseOutputPin_from_IPin(iface); 501 502 TRACE("()\n"); 503 504 EnterCriticalSection(This->pin.pCritSec); 505 { 506 if (This->pMemInputPin) 507 { 508 IMemInputPin_Release(This->pMemInputPin); 509 This->pMemInputPin = NULL; 510 } 511 if (This->pin.pConnectedTo) 512 { 513 IPin_Release(This->pin.pConnectedTo); 514 This->pin.pConnectedTo = NULL; 515 FreeMediaType(&This->pin.mtCurrent); 516 ZeroMemory(&This->pin.mtCurrent, sizeof(This->pin.mtCurrent)); 517 hr = S_OK; 518 } 519 else 520 hr = S_FALSE; 521 } 522 LeaveCriticalSection(This->pin.pCritSec); 523 524 return hr; 525 } 526 527 HRESULT WINAPI BaseOutputPinImpl_EndOfStream(IPin * iface) 528 { 529 TRACE("()\n"); 530 531 /* not supposed to do anything in an output pin */ 532 533 return E_UNEXPECTED; 534 } 535 536 HRESULT WINAPI BaseOutputPinImpl_BeginFlush(IPin * iface) 537 { 538 TRACE("(%p)->()\n", iface); 539 540 /* not supposed to do anything in an output pin */ 541 542 return E_UNEXPECTED; 543 } 544 545 HRESULT WINAPI BaseOutputPinImpl_EndFlush(IPin * iface) 546 { 547 TRACE("(%p)->()\n", iface); 548 549 /* not supposed to do anything in an output pin */ 550 551 return E_UNEXPECTED; 552 } 553 554 HRESULT WINAPI BaseOutputPinImpl_GetDeliveryBuffer(BaseOutputPin *This, IMediaSample ** ppSample, REFERENCE_TIME * tStart, REFERENCE_TIME * tStop, DWORD dwFlags) 555 { 556 HRESULT hr; 557 558 TRACE("(%p, %p, %p, %x)\n", ppSample, tStart, tStop, dwFlags); 559 560 if (!This->pin.pConnectedTo) 561 hr = VFW_E_NOT_CONNECTED; 562 else 563 { 564 hr = IMemAllocator_GetBuffer(This->pAllocator, ppSample, tStart, tStop, dwFlags); 565 566 if (SUCCEEDED(hr)) 567 hr = IMediaSample_SetTime(*ppSample, tStart, tStop); 568 } 569 570 return hr; 571 } 572 573 /* replaces OutputPin_SendSample */ 574 HRESULT WINAPI BaseOutputPinImpl_Deliver(BaseOutputPin *This, IMediaSample * pSample) 575 { 576 IMemInputPin * pMemConnected = NULL; 577 PIN_INFO pinInfo; 578 HRESULT hr; 579 580 EnterCriticalSection(This->pin.pCritSec); 581 { 582 if (!This->pin.pConnectedTo || !This->pMemInputPin) 583 hr = VFW_E_NOT_CONNECTED; 584 else 585 { 586 /* we don't have the lock held when using This->pMemInputPin, 587 * so we need to AddRef it to stop it being deleted while we are 588 * using it. Same with its filter. */ 589 pMemConnected = This->pMemInputPin; 590 IMemInputPin_AddRef(pMemConnected); 591 hr = IPin_QueryPinInfo(This->pin.pConnectedTo, &pinInfo); 592 } 593 } 594 LeaveCriticalSection(This->pin.pCritSec); 595 596 if (SUCCEEDED(hr)) 597 { 598 /* NOTE: if we are in a critical section when Receive is called 599 * then it causes some problems (most notably with the native Video 600 * Renderer) if we are re-entered for whatever reason */ 601 hr = IMemInputPin_Receive(pMemConnected, pSample); 602 603 /* If the filter's destroyed, tell upstream to stop sending data */ 604 if(IBaseFilter_Release(pinInfo.pFilter) == 0 && SUCCEEDED(hr)) 605 hr = S_FALSE; 606 } 607 if (pMemConnected) 608 IMemInputPin_Release(pMemConnected); 609 610 return hr; 611 } 612 613 /* replaces OutputPin_CommitAllocator */ 614 HRESULT WINAPI BaseOutputPinImpl_Active(BaseOutputPin *This) 615 { 616 HRESULT hr; 617 618 TRACE("(%p)->()\n", This); 619 620 EnterCriticalSection(This->pin.pCritSec); 621 { 622 if (!This->pin.pConnectedTo || !This->pMemInputPin) 623 hr = VFW_E_NOT_CONNECTED; 624 else 625 hr = IMemAllocator_Commit(This->pAllocator); 626 } 627 LeaveCriticalSection(This->pin.pCritSec); 628 629 TRACE("--> %08x\n", hr); 630 return hr; 631 } 632 633 /* replaces OutputPin_DecommitAllocator */ 634 HRESULT WINAPI BaseOutputPinImpl_Inactive(BaseOutputPin *This) 635 { 636 HRESULT hr; 637 638 TRACE("(%p)->()\n", This); 639 640 EnterCriticalSection(This->pin.pCritSec); 641 { 642 if (!This->pin.pConnectedTo || !This->pMemInputPin) 643 hr = VFW_E_NOT_CONNECTED; 644 else 645 hr = IMemAllocator_Decommit(This->pAllocator); 646 } 647 LeaveCriticalSection(This->pin.pCritSec); 648 649 TRACE("--> %08x\n", hr); 650 return hr; 651 } 652 653 /* replaces OutputPin_DeliverDisconnect */ 654 HRESULT WINAPI BaseOutputPinImpl_BreakConnect(BaseOutputPin *This) 655 { 656 HRESULT hr; 657 658 TRACE("(%p)->()\n", This); 659 660 EnterCriticalSection(This->pin.pCritSec); 661 { 662 if (!This->pin.pConnectedTo || !This->pMemInputPin) 663 hr = VFW_E_NOT_CONNECTED; 664 else 665 { 666 hr = IMemAllocator_Decommit(This->pAllocator); 667 668 if (SUCCEEDED(hr)) 669 hr = IPin_Disconnect(This->pin.pConnectedTo); 670 } 671 IPin_Disconnect(&This->pin.IPin_iface); 672 } 673 LeaveCriticalSection(This->pin.pCritSec); 674 675 return hr; 676 } 677 678 HRESULT WINAPI BaseOutputPinImpl_InitAllocator(BaseOutputPin *This, IMemAllocator **pMemAlloc) 679 { 680 return CoCreateInstance(&CLSID_MemoryAllocator, NULL, CLSCTX_INPROC_SERVER, &IID_IMemAllocator, (LPVOID*)pMemAlloc); 681 } 682 683 HRESULT WINAPI BaseOutputPinImpl_DecideAllocator(BaseOutputPin *This, IMemInputPin *pPin, IMemAllocator **pAlloc) 684 { 685 HRESULT hr; 686 687 hr = IMemInputPin_GetAllocator(pPin, pAlloc); 688 689 if (hr == VFW_E_NO_ALLOCATOR) 690 /* Input pin provides no allocator, use standard memory allocator */ 691 hr = BaseOutputPinImpl_InitAllocator(This, pAlloc); 692 693 if (SUCCEEDED(hr)) 694 { 695 ALLOCATOR_PROPERTIES rProps; 696 ZeroMemory(&rProps, sizeof(ALLOCATOR_PROPERTIES)); 697 698 IMemInputPin_GetAllocatorRequirements(pPin, &rProps); 699 hr = This->pFuncsTable->pfnDecideBufferSize(This, *pAlloc, &rProps); 700 } 701 702 if (SUCCEEDED(hr)) 703 hr = IMemInputPin_NotifyAllocator(pPin, *pAlloc, FALSE); 704 705 return hr; 706 } 707 708 /*** The Construct functions ***/ 709 710 /* Function called as a helper to IPin_Connect */ 711 /* specific AM_MEDIA_TYPE - it cannot be NULL */ 712 HRESULT WINAPI BaseOutputPinImpl_AttemptConnection(BasePin* iface, IPin * pReceivePin, const AM_MEDIA_TYPE * pmt) 713 { 714 BaseOutputPin *This = impl_BaseOutputPin_from_BasePin(iface); 715 HRESULT hr; 716 IMemAllocator * pMemAlloc = NULL; 717 718 TRACE("(%p, %p)\n", pReceivePin, pmt); 719 dump_AM_MEDIA_TYPE(pmt); 720 721 /* FIXME: call queryacceptproc */ 722 723 This->pin.pConnectedTo = pReceivePin; 724 IPin_AddRef(pReceivePin); 725 CopyMediaType(&This->pin.mtCurrent, pmt); 726 727 hr = IPin_ReceiveConnection(pReceivePin, &iface->IPin_iface, pmt); 728 729 /* get the IMemInputPin interface we will use to deliver samples to the 730 * connected pin */ 731 if (SUCCEEDED(hr)) 732 { 733 This->pMemInputPin = NULL; 734 hr = IPin_QueryInterface(pReceivePin, &IID_IMemInputPin, (LPVOID)&This->pMemInputPin); 735 736 if (SUCCEEDED(hr)) 737 { 738 hr = This->pFuncsTable->pfnDecideAllocator(This, This->pMemInputPin, &pMemAlloc); 739 if (SUCCEEDED(hr)) 740 This->pAllocator = pMemAlloc; 741 else if (pMemAlloc) 742 IMemAllocator_Release(pMemAlloc); 743 } 744 745 /* break connection if we couldn't get the allocator */ 746 if (FAILED(hr)) 747 { 748 if (This->pMemInputPin) 749 IMemInputPin_Release(This->pMemInputPin); 750 This->pMemInputPin = NULL; 751 752 IPin_Disconnect(pReceivePin); 753 } 754 } 755 756 if (FAILED(hr)) 757 { 758 IPin_Release(This->pin.pConnectedTo); 759 This->pin.pConnectedTo = NULL; 760 FreeMediaType(&This->pin.mtCurrent); 761 } 762 763 TRACE(" -- %x\n", hr); 764 return hr; 765 } 766 767 static HRESULT OutputPin_Init(const IPinVtbl *OutputPin_Vtbl, const PIN_INFO * pPinInfo, const BaseOutputPinFuncTable* vtbl, LPCRITICAL_SECTION pCritSec, BaseOutputPin * pPinImpl) 768 { 769 TRACE("\n"); 770 771 /* Common attributes */ 772 pPinImpl->pin.IPin_iface.lpVtbl = OutputPin_Vtbl; 773 pPinImpl->pin.refCount = 1; 774 pPinImpl->pin.pConnectedTo = NULL; 775 pPinImpl->pin.pCritSec = pCritSec; 776 pPinImpl->pin.tStart = 0; 777 pPinImpl->pin.tStop = 0; 778 pPinImpl->pin.dRate = 1.0; 779 Copy_PinInfo(&pPinImpl->pin.pinInfo, pPinInfo); 780 pPinImpl->pin.pFuncsTable = &vtbl->base; 781 ZeroMemory(&pPinImpl->pin.mtCurrent, sizeof(AM_MEDIA_TYPE)); 782 783 /* Output pin attributes */ 784 pPinImpl->pMemInputPin = NULL; 785 pPinImpl->pAllocator = NULL; 786 pPinImpl->pFuncsTable = vtbl; 787 788 return S_OK; 789 } 790 791 HRESULT WINAPI BaseOutputPin_Construct(const IPinVtbl *OutputPin_Vtbl, LONG outputpin_size, const PIN_INFO * pPinInfo, const BaseOutputPinFuncTable* vtbl, LPCRITICAL_SECTION pCritSec, IPin ** ppPin) 792 { 793 BaseOutputPin * pPinImpl; 794 795 *ppPin = NULL; 796 797 if (pPinInfo->dir != PINDIR_OUTPUT) 798 { 799 ERR("Pin direction(%x) != PINDIR_OUTPUT\n", pPinInfo->dir); 800 return E_INVALIDARG; 801 } 802 803 assert(outputpin_size >= sizeof(BaseOutputPin)); 804 assert(vtbl->base.pfnAttemptConnection); 805 806 pPinImpl = CoTaskMemAlloc(outputpin_size); 807 808 if (!pPinImpl) 809 return E_OUTOFMEMORY; 810 811 if (SUCCEEDED(OutputPin_Init(OutputPin_Vtbl, pPinInfo, vtbl, pCritSec, pPinImpl))) 812 { 813 *ppPin = &pPinImpl->pin.IPin_iface; 814 return S_OK; 815 } 816 817 CoTaskMemFree(pPinImpl); 818 return E_FAIL; 819 } 820 821 HRESULT WINAPI BaseOutputPin_Destroy(BaseOutputPin *This) 822 { 823 FreeMediaType(&This->pin.mtCurrent); 824 if (This->pAllocator) 825 IMemAllocator_Release(This->pAllocator); 826 This->pAllocator = NULL; 827 CoTaskMemFree(This); 828 return S_OK; 829 } 830 831 /*** Input Pin implementation ***/ 832 833 static inline BaseInputPin *impl_BaseInputPin_from_IPin( IPin *iface ) 834 { 835 return CONTAINING_RECORD(iface, BaseInputPin, pin.IPin_iface); 836 } 837 838 HRESULT WINAPI BaseInputPinImpl_QueryInterface(IPin * iface, REFIID riid, LPVOID * ppv) 839 { 840 BaseInputPin *This = impl_BaseInputPin_from_IPin(iface); 841 842 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv); 843 844 *ppv = NULL; 845 846 if (IsEqualIID(riid, &IID_IUnknown)) 847 *ppv = iface; 848 else if (IsEqualIID(riid, &IID_IPin)) 849 *ppv = iface; 850 else if (IsEqualIID(riid, &IID_IMemInputPin)) 851 *ppv = &This->IMemInputPin_iface; 852 else if (IsEqualIID(riid, &IID_IMediaSeeking)) 853 { 854 return IBaseFilter_QueryInterface(This->pin.pinInfo.pFilter, &IID_IMediaSeeking, ppv); 855 } 856 857 if (*ppv) 858 { 859 IUnknown_AddRef((IUnknown *)(*ppv)); 860 return S_OK; 861 } 862 863 FIXME("No interface for %s!\n", debugstr_guid(riid)); 864 865 return E_NOINTERFACE; 866 } 867 868 ULONG WINAPI BaseInputPinImpl_Release(IPin * iface) 869 { 870 BaseInputPin *This = impl_BaseInputPin_from_IPin(iface); 871 ULONG refCount = InterlockedDecrement(&This->pin.refCount); 872 873 TRACE("(%p)->() Release from %d\n", iface, refCount + 1); 874 875 if (!refCount) 876 BaseInputPin_Destroy(This); 877 878 return refCount; 879 } 880 881 HRESULT WINAPI BaseInputPinImpl_Connect(IPin * iface, IPin * pConnector, const AM_MEDIA_TYPE * pmt) 882 { 883 ERR("Outgoing connection on an input pin! (%p, %p)\n", pConnector, pmt); 884 885 return E_UNEXPECTED; 886 } 887 888 889 HRESULT WINAPI BaseInputPinImpl_ReceiveConnection(IPin * iface, IPin * pReceivePin, const AM_MEDIA_TYPE * pmt) 890 { 891 BaseInputPin *This = impl_BaseInputPin_from_IPin(iface); 892 PIN_DIRECTION pindirReceive; 893 HRESULT hr = S_OK; 894 895 TRACE("(%p, %p)\n", pReceivePin, pmt); 896 dump_AM_MEDIA_TYPE(pmt); 897 898 EnterCriticalSection(This->pin.pCritSec); 899 { 900 if (This->pin.pConnectedTo) 901 hr = VFW_E_ALREADY_CONNECTED; 902 903 if (SUCCEEDED(hr) && This->pin.pFuncsTable->pfnCheckMediaType(&This->pin, pmt) != S_OK) 904 hr = VFW_E_TYPE_NOT_ACCEPTED; /* FIXME: shouldn't we just map common errors onto 905 * VFW_E_TYPE_NOT_ACCEPTED and pass the value on otherwise? */ 906 907 if (SUCCEEDED(hr)) 908 { 909 IPin_QueryDirection(pReceivePin, &pindirReceive); 910 911 if (pindirReceive != PINDIR_OUTPUT) 912 { 913 ERR("Can't connect from non-output pin\n"); 914 hr = VFW_E_INVALID_DIRECTION; 915 } 916 } 917 918 if (SUCCEEDED(hr)) 919 { 920 CopyMediaType(&This->pin.mtCurrent, pmt); 921 This->pin.pConnectedTo = pReceivePin; 922 IPin_AddRef(pReceivePin); 923 } 924 } 925 LeaveCriticalSection(This->pin.pCritSec); 926 927 return hr; 928 } 929 930 static HRESULT deliver_endofstream(IPin* pin, LPVOID unused) 931 { 932 return IPin_EndOfStream( pin ); 933 } 934 935 HRESULT WINAPI BaseInputPinImpl_QueryAccept(IPin * iface, const AM_MEDIA_TYPE * pmt) 936 { 937 BaseInputPin *This = impl_BaseInputPin_from_IPin(iface); 938 939 TRACE("(%p/%p)->(%p)\n", This, iface, pmt); 940 941 return (This->pin.pFuncsTable->pfnCheckMediaType(&This->pin, pmt) == S_OK ? S_OK : S_FALSE); 942 } 943 944 HRESULT WINAPI BaseInputPinImpl_EndOfStream(IPin * iface) 945 { 946 HRESULT hr = S_OK; 947 BaseInputPin *This = impl_BaseInputPin_from_IPin(iface); 948 949 TRACE("(%p)\n", This); 950 951 EnterCriticalSection(This->pin.pCritSec); 952 if (This->flushing) 953 hr = S_FALSE; 954 else 955 This->end_of_stream = TRUE; 956 LeaveCriticalSection(This->pin.pCritSec); 957 958 if (hr == S_OK) 959 hr = SendFurther( iface, deliver_endofstream, NULL, NULL ); 960 return hr; 961 } 962 963 static HRESULT deliver_beginflush(IPin* pin, LPVOID unused) 964 { 965 return IPin_BeginFlush( pin ); 966 } 967 968 HRESULT WINAPI BaseInputPinImpl_BeginFlush(IPin * iface) 969 { 970 BaseInputPin *This = impl_BaseInputPin_from_IPin(iface); 971 HRESULT hr; 972 TRACE("() semi-stub\n"); 973 974 EnterCriticalSection(This->pin.pCritSec); 975 This->flushing = TRUE; 976 977 hr = SendFurther( iface, deliver_beginflush, NULL, NULL ); 978 LeaveCriticalSection(This->pin.pCritSec); 979 980 return hr; 981 } 982 983 static HRESULT deliver_endflush(IPin* pin, LPVOID unused) 984 { 985 return IPin_EndFlush( pin ); 986 } 987 988 HRESULT WINAPI BaseInputPinImpl_EndFlush(IPin * iface) 989 { 990 BaseInputPin *This = impl_BaseInputPin_from_IPin(iface); 991 HRESULT hr; 992 TRACE("(%p)\n", This); 993 994 EnterCriticalSection(This->pin.pCritSec); 995 This->flushing = This->end_of_stream = FALSE; 996 997 hr = SendFurther( iface, deliver_endflush, NULL, NULL ); 998 LeaveCriticalSection(This->pin.pCritSec); 999 1000 return hr; 1001 } 1002 1003 typedef struct newsegmentargs 1004 { 1005 REFERENCE_TIME tStart, tStop; 1006 double rate; 1007 } newsegmentargs; 1008 1009 static HRESULT deliver_newsegment(IPin *pin, LPVOID data) 1010 { 1011 newsegmentargs *args = data; 1012 return IPin_NewSegment(pin, args->tStart, args->tStop, args->rate); 1013 } 1014 1015 HRESULT WINAPI BaseInputPinImpl_NewSegment(IPin * iface, REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate) 1016 { 1017 BaseInputPin *This = impl_BaseInputPin_from_IPin(iface); 1018 newsegmentargs args; 1019 1020 TRACE("(%s, %s, %e)\n", wine_dbgstr_longlong(tStart), wine_dbgstr_longlong(tStop), dRate); 1021 1022 args.tStart = This->pin.tStart = tStart; 1023 args.tStop = This->pin.tStop = tStop; 1024 args.rate = This->pin.dRate = dRate; 1025 1026 return SendFurther( iface, deliver_newsegment, &args, NULL ); 1027 } 1028 1029 /*** IMemInputPin implementation ***/ 1030 1031 static inline BaseInputPin *impl_from_IMemInputPin( IMemInputPin *iface ) 1032 { 1033 return CONTAINING_RECORD(iface, BaseInputPin, IMemInputPin_iface); 1034 } 1035 1036 static HRESULT WINAPI MemInputPin_QueryInterface(IMemInputPin * iface, REFIID riid, LPVOID * ppv) 1037 { 1038 BaseInputPin *This = impl_from_IMemInputPin(iface); 1039 1040 return IPin_QueryInterface(&This->pin.IPin_iface, riid, ppv); 1041 } 1042 1043 static ULONG WINAPI MemInputPin_AddRef(IMemInputPin * iface) 1044 { 1045 BaseInputPin *This = impl_from_IMemInputPin(iface); 1046 1047 return IPin_AddRef(&This->pin.IPin_iface); 1048 } 1049 1050 static ULONG WINAPI MemInputPin_Release(IMemInputPin * iface) 1051 { 1052 BaseInputPin *This = impl_from_IMemInputPin(iface); 1053 1054 return IPin_Release(&This->pin.IPin_iface); 1055 } 1056 1057 static HRESULT WINAPI MemInputPin_GetAllocator(IMemInputPin * iface, IMemAllocator ** ppAllocator) 1058 { 1059 BaseInputPin *This = impl_from_IMemInputPin(iface); 1060 1061 TRACE("(%p/%p)->(%p)\n", This, iface, ppAllocator); 1062 1063 *ppAllocator = This->pAllocator; 1064 if (*ppAllocator) 1065 IMemAllocator_AddRef(*ppAllocator); 1066 1067 return *ppAllocator ? S_OK : VFW_E_NO_ALLOCATOR; 1068 } 1069 1070 static HRESULT WINAPI MemInputPin_NotifyAllocator(IMemInputPin * iface, IMemAllocator * pAllocator, BOOL bReadOnly) 1071 { 1072 BaseInputPin *This = impl_from_IMemInputPin(iface); 1073 1074 TRACE("(%p/%p)->(%p, %d)\n", This, iface, pAllocator, bReadOnly); 1075 1076 if (bReadOnly) 1077 FIXME("Read only flag not handled yet!\n"); 1078 1079 /* FIXME: Should we release the allocator on disconnection? */ 1080 if (!pAllocator) 1081 { 1082 WARN("Null allocator\n"); 1083 return E_POINTER; 1084 } 1085 1086 if (This->preferred_allocator && pAllocator != This->preferred_allocator) 1087 return E_FAIL; 1088 1089 if (This->pAllocator) 1090 IMemAllocator_Release(This->pAllocator); 1091 This->pAllocator = pAllocator; 1092 if (This->pAllocator) 1093 IMemAllocator_AddRef(This->pAllocator); 1094 1095 return S_OK; 1096 } 1097 1098 static HRESULT WINAPI MemInputPin_GetAllocatorRequirements(IMemInputPin * iface, ALLOCATOR_PROPERTIES * pProps) 1099 { 1100 BaseInputPin *This = impl_from_IMemInputPin(iface); 1101 1102 TRACE("(%p/%p)->(%p)\n", This, iface, pProps); 1103 1104 /* override this method if you have any specific requirements */ 1105 1106 return E_NOTIMPL; 1107 } 1108 1109 static HRESULT WINAPI MemInputPin_Receive(IMemInputPin * iface, IMediaSample * pSample) 1110 { 1111 BaseInputPin *This = impl_from_IMemInputPin(iface); 1112 HRESULT hr = S_FALSE; 1113 1114 /* this trace commented out for performance reasons */ 1115 /*TRACE("(%p/%p)->(%p)\n", This, iface, pSample);*/ 1116 if (This->pFuncsTable->pfnReceive) 1117 hr = This->pFuncsTable->pfnReceive(This, pSample); 1118 return hr; 1119 } 1120 1121 static HRESULT WINAPI MemInputPin_ReceiveMultiple(IMemInputPin * iface, IMediaSample ** pSamples, LONG nSamples, LONG *nSamplesProcessed) 1122 { 1123 HRESULT hr = S_OK; 1124 BaseInputPin *This = impl_from_IMemInputPin(iface); 1125 1126 TRACE("(%p/%p)->(%p, %d, %p)\n", This, iface, pSamples, nSamples, nSamplesProcessed); 1127 1128 for (*nSamplesProcessed = 0; *nSamplesProcessed < nSamples; (*nSamplesProcessed)++) 1129 { 1130 hr = IMemInputPin_Receive(iface, pSamples[*nSamplesProcessed]); 1131 if (hr != S_OK) 1132 break; 1133 } 1134 1135 return hr; 1136 } 1137 1138 static HRESULT WINAPI MemInputPin_ReceiveCanBlock(IMemInputPin * iface) 1139 { 1140 BaseInputPin *This = impl_from_IMemInputPin(iface); 1141 1142 TRACE("(%p/%p)->()\n", This, iface); 1143 1144 return S_OK; 1145 } 1146 1147 static const IMemInputPinVtbl MemInputPin_Vtbl = 1148 { 1149 MemInputPin_QueryInterface, 1150 MemInputPin_AddRef, 1151 MemInputPin_Release, 1152 MemInputPin_GetAllocator, 1153 MemInputPin_NotifyAllocator, 1154 MemInputPin_GetAllocatorRequirements, 1155 MemInputPin_Receive, 1156 MemInputPin_ReceiveMultiple, 1157 MemInputPin_ReceiveCanBlock 1158 }; 1159 1160 static HRESULT InputPin_Init(const IPinVtbl *InputPin_Vtbl, const PIN_INFO * pPinInfo, 1161 const BaseInputPinFuncTable* vtbl, 1162 LPCRITICAL_SECTION pCritSec, IMemAllocator *allocator, BaseInputPin * pPinImpl) 1163 { 1164 TRACE("\n"); 1165 1166 /* Common attributes */ 1167 pPinImpl->pin.refCount = 1; 1168 pPinImpl->pin.pConnectedTo = NULL; 1169 pPinImpl->pin.pCritSec = pCritSec; 1170 pPinImpl->pin.tStart = 0; 1171 pPinImpl->pin.tStop = 0; 1172 pPinImpl->pin.dRate = 1.0; 1173 Copy_PinInfo(&pPinImpl->pin.pinInfo, pPinInfo); 1174 ZeroMemory(&pPinImpl->pin.mtCurrent, sizeof(AM_MEDIA_TYPE)); 1175 pPinImpl->pin.pFuncsTable = &vtbl->base; 1176 1177 /* Input pin attributes */ 1178 pPinImpl->pFuncsTable = vtbl; 1179 pPinImpl->pAllocator = pPinImpl->preferred_allocator = allocator; 1180 if (pPinImpl->preferred_allocator) 1181 IMemAllocator_AddRef(pPinImpl->preferred_allocator); 1182 pPinImpl->pin.IPin_iface.lpVtbl = InputPin_Vtbl; 1183 pPinImpl->IMemInputPin_iface.lpVtbl = &MemInputPin_Vtbl; 1184 pPinImpl->flushing = pPinImpl->end_of_stream = FALSE; 1185 1186 return S_OK; 1187 } 1188 1189 HRESULT BaseInputPin_Construct(const IPinVtbl *InputPin_Vtbl, LONG inputpin_size, const PIN_INFO * pPinInfo, 1190 const BaseInputPinFuncTable* vtbl, 1191 LPCRITICAL_SECTION pCritSec, IMemAllocator *allocator, IPin ** ppPin) 1192 { 1193 BaseInputPin * pPinImpl; 1194 1195 *ppPin = NULL; 1196 1197 assert(inputpin_size >= sizeof(BaseInputPin)); 1198 assert(vtbl->base.pfnCheckMediaType); 1199 1200 if (pPinInfo->dir != PINDIR_INPUT) 1201 { 1202 ERR("Pin direction(%x) != PINDIR_INPUT\n", pPinInfo->dir); 1203 return E_INVALIDARG; 1204 } 1205 1206 pPinImpl = CoTaskMemAlloc(inputpin_size); 1207 1208 if (!pPinImpl) 1209 return E_OUTOFMEMORY; 1210 1211 if (SUCCEEDED(InputPin_Init(InputPin_Vtbl, pPinInfo, vtbl, pCritSec, allocator, pPinImpl))) 1212 { 1213 *ppPin = &pPinImpl->pin.IPin_iface; 1214 return S_OK; 1215 } 1216 1217 CoTaskMemFree(pPinImpl); 1218 return E_FAIL; 1219 } 1220 1221 HRESULT WINAPI BaseInputPin_Destroy(BaseInputPin *This) 1222 { 1223 FreeMediaType(&This->pin.mtCurrent); 1224 if (This->pAllocator) 1225 IMemAllocator_Release(This->pAllocator); 1226 This->pAllocator = NULL; 1227 This->pin.IPin_iface.lpVtbl = NULL; 1228 CoTaskMemFree(This); 1229 return S_OK; 1230 } 1231