void Parser_Destroy(ParserImpl *This) { IPin *connected = NULL; ULONG pinref; assert(!This->filter.refCount); PullPin_WaitForStateChange(This->pInputPin, INFINITE); /* Don't need to clean up output pins, freeing input pin will do that */ IPin_ConnectedTo(&This->pInputPin->pin.IPin_iface, &connected); if (connected) { assert(IPin_Disconnect(connected) == S_OK); IPin_Release(connected); assert(IPin_Disconnect(&This->pInputPin->pin.IPin_iface) == S_OK); } pinref = IPin_Release(&This->pInputPin->pin.IPin_iface); if (pinref) { /* Valgrind could find this, if I kill it here */ ERR("pinref should be null, is %u, destroying anyway\n", pinref); assert((LONG)pinref > 0); while (pinref) pinref = IPin_Release(&This->pInputPin->pin.IPin_iface); } CoTaskMemFree(This->ppPins); TRACE("Destroying parser\n"); CoTaskMemFree(This); }
ULONG WINAPI BaseRendererImpl_Release(IBaseFilter* iface) { BaseRenderer *This = impl_from_IBaseFilter(iface); ULONG refCount = BaseFilterImpl_Release(iface); if (!refCount) { IPin *pConnectedTo; if (SUCCEEDED(IPin_ConnectedTo(&This->pInputPin->pin.IPin_iface, &pConnectedTo))) { IPin_Disconnect(pConnectedTo); IPin_Release(pConnectedTo); } IPin_Disconnect(&This->pInputPin->pin.IPin_iface); IPin_Release(&This->pInputPin->pin.IPin_iface); if (This->pPosition) IUnknown_Release(This->pPosition); This->csRenderLock.DebugInfo->Spare[0] = 0; DeleteCriticalSection(&This->csRenderLock); BaseRendererImpl_ClearPendingSample(This); CloseHandle(This->evComplete); CloseHandle(This->ThreadSignal); CloseHandle(This->RenderEvent); QualityControlImpl_Destroy(This->qcimpl); } return refCount; }
static ULONG WINAPI unknown_inner_Release(IUnknown *iface) { VfwCapture *This = impl_from_IUnknown(iface); ULONG ref = InterlockedDecrement(&This->filter.refCount); TRACE("(%p) ref=%d\n", This, ref); if (!ref) { IPin *conn = NULL; TRACE("destroying everything\n"); if (This->init) { if (This->filter.state != State_Stopped) qcap_driver_stop(This->driver_info, &This->filter.state); qcap_driver_destroy(This->driver_info); } IPin_ConnectedTo(This->pOutputPin, &conn); if (conn) { IPin_Disconnect(conn); IPin_Disconnect(This->pOutputPin); IPin_Release(conn); } IPin_Release(This->pOutputPin); BaseFilter_Destroy(&This->filter); CoTaskMemFree(This); ObjectRefCount(FALSE); } return ref; }
static ULONG WINAPI NullRendererInner_Release(IUnknown * iface) { ICOM_THIS_MULTI(NullRendererImpl, IInner_vtbl, iface); ULONG refCount = InterlockedDecrement(&This->refCount); TRACE("(%p/%p)->() Release from %d\n", This, iface, refCount + 1); if (!refCount) { IPin *pConnectedTo; if (This->pClock) IReferenceClock_Release(This->pClock); if (SUCCEEDED(IPin_ConnectedTo((IPin *)This->pInputPin, &pConnectedTo))) { IPin_Disconnect(pConnectedTo); IPin_Release(pConnectedTo); } IPin_Disconnect((IPin *)This->pInputPin); IPin_Release((IPin *)This->pInputPin); This->lpVtbl = NULL; This->csFilter.DebugInfo->Spare[0] = 0; DeleteCriticalSection(&This->csFilter); TRACE("Destroying Null Renderer\n"); CoTaskMemFree(This); return 0; } else return refCount; }
static HRESULT WINAPI QTInPin_Disconnect(IPin *iface) { HRESULT hr; QTInPin *This = impl_from_IPin(iface); FILTER_STATE state; TRACE("()\n"); hr = IBaseFilter_GetState(This->pin.pinInfo.pFilter, INFINITE, &state); EnterCriticalSection(This->pin.pCritSec); if (This->pin.pConnectedTo) { QTSplitter *Parser = impl_from_IBaseFilter(This->pin.pinInfo.pFilter); if (SUCCEEDED(hr) && state == State_Stopped) { IMemAllocator_Decommit(This->pAlloc); IPin_Disconnect(This->pin.pConnectedTo); This->pin.pConnectedTo = NULL; hr = QT_RemoveOutputPins(Parser); } else hr = VFW_E_NOT_STOPPED; } else hr = S_FALSE; LeaveCriticalSection(This->pin.pCritSec); return hr; }
static ULONG WINAPI DSoundRender_Release(IBaseFilter * iface) { DSoundRenderImpl *This = (DSoundRenderImpl *)iface; ULONG refCount = InterlockedDecrement(&This->refCount); TRACE("(%p)->() Release from %d\n", This, refCount + 1); if (!refCount) { IPin *pConnectedTo; if (This->pClock) IReferenceClock_Release(This->pClock); if (This->dsbuffer) IDirectSoundBuffer_Release(This->dsbuffer); This->dsbuffer = NULL; if (This->dsound) IDirectSound_Release(This->dsound); This->dsound = NULL; if (SUCCEEDED(IPin_ConnectedTo((IPin *)This->pInputPin, &pConnectedTo))) { IPin_Disconnect(pConnectedTo); IPin_Release(pConnectedTo); } IPin_Disconnect((IPin *)This->pInputPin); IPin_Release((IPin *)This->pInputPin); This->lpVtbl = NULL; This->IBasicAudio_vtbl = NULL; This->csFilter.DebugInfo->Spare[0] = 0; DeleteCriticalSection(&This->csFilter); CloseHandle(This->state_change); CloseHandle(This->blocked); TRACE("Destroying Audio Renderer\n"); CoTaskMemFree(This); return 0; } else return refCount; }
static void QT_Destroy(QTSplitter *This) { IPin *connected = NULL; ULONG pinref; TRACE("Destroying\n"); EnterCriticalSection(&This->csReceive); /* Don't need to clean up output pins, disconnecting input pin will do that */ IPin_ConnectedTo(&This->pInputPin.pin.IPin_iface, &connected); if (connected) { IPin_Disconnect(connected); IPin_Release(connected); } pinref = IPin_Release(&This->pInputPin.pin.IPin_iface); if (pinref) { ERR("pinref should be null, is %u, destroying anyway\n", pinref); assert((LONG)pinref > 0); while (pinref) pinref = IPin_Release(&This->pInputPin.pin.IPin_iface); } if (This->pQTMovie) { DisposeMovie(This->pQTMovie); This->pQTMovie = NULL; } if (This->vContext) QTVisualContextRelease(This->vContext); if (This->aSession) MovieAudioExtractionEnd(This->aSession); ExitMoviesOnThread(); LeaveCriticalSection(&This->csReceive); if (This->loaderThread) { WaitForSingleObject(This->loaderThread, INFINITE); CloseHandle(This->loaderThread); } if (This->splitterThread) { SetEvent(This->runEvent); WaitForSingleObject(This->splitterThread, INFINITE); CloseHandle(This->splitterThread); } CloseHandle(This->runEvent); This->csReceive.DebugInfo->Spare[0] = 0; DeleteCriticalSection(&This->csReceive); BaseFilter_Destroy(&This->filter); CoTaskMemFree(This); }
static HRESULT WINAPI QTOutPin_BreakConnect(BaseOutputPin *This) { HRESULT hr; TRACE("(%p)->()\n", This); EnterCriticalSection(This->pin.pCritSec); if (!This->pin.pConnectedTo || !This->pMemInputPin) hr = VFW_E_NOT_CONNECTED; else { hr = IPin_Disconnect(This->pin.pConnectedTo); IPin_Disconnect(&This->pin.IPin_iface); } LeaveCriticalSection(This->pin.pCritSec); return hr; }
static ULONG WINAPI TransformFilter_Release(IBaseFilter * iface) { TransformFilterImpl *This = (TransformFilterImpl *)iface; ULONG refCount = InterlockedDecrement(&This->refCount); TRACE("(%p/%p)->() Release from %d\n", This, iface, refCount + 1); if (!refCount) { ULONG i; if (This->pClock) IReferenceClock_Release(This->pClock); for (i = 0; i < This->npins; i++) { IPin *pConnectedTo; if (SUCCEEDED(IPin_ConnectedTo(This->ppPins[i], &pConnectedTo))) { IPin_Disconnect(pConnectedTo); IPin_Release(pConnectedTo); } IPin_Disconnect(This->ppPins[i]); IPin_Release(This->ppPins[i]); } CoTaskMemFree(This->ppPins); This->lpVtbl = NULL; This->csFilter.DebugInfo->Spare[0] = 0; DeleteCriticalSection(&This->csFilter); TRACE("Destroying transform filter\n"); FreeMediaType(&This->pmt); CoTaskMemFree(This); return 0; } else return refCount; }
static ULONG WINAPI DSoundRender_Release(IBaseFilter * iface) { DSoundRenderImpl *This = (DSoundRenderImpl *)iface; ULONG refCount = BaseFilterImpl_Release(iface); TRACE("(%p)->() Release from %d\n", This, refCount + 1); if (!refCount) { IPin *pConnectedTo; if (This->dsbuffer) IDirectSoundBuffer_Release(This->dsbuffer); This->dsbuffer = NULL; if (This->dsound) IDirectSound_Release(This->dsound); This->dsound = NULL; if (SUCCEEDED(IPin_ConnectedTo((IPin *)This->pInputPin, &pConnectedTo))) { IPin_Disconnect(pConnectedTo); IPin_Release(pConnectedTo); } IPin_Disconnect((IPin *)This->pInputPin); IPin_Release((IPin *)This->pInputPin); This->IBasicAudio_vtbl = NULL; if (This->seekthru_unk) IUnknown_Release(This->seekthru_unk); CloseHandle(This->state_change); CloseHandle(This->blocked); TRACE("Destroying Audio Renderer\n"); CoTaskMemFree(This); return 0; } else return refCount; }
ULONG WINAPI TransformFilterImpl_Release(IBaseFilter * iface) { TransformFilter *This = impl_from_IBaseFilter(iface); ULONG refCount = InterlockedDecrement(&This->filter.refCount); TRACE("(%p/%p)->() Release from %d\n", This, iface, refCount + 1); if (!refCount) { ULONG i; for (i = 0; i < This->npins; i++) { IPin *pConnectedTo; if (SUCCEEDED(IPin_ConnectedTo(This->ppPins[i], &pConnectedTo))) { IPin_Disconnect(pConnectedTo); IPin_Release(pConnectedTo); } IPin_Disconnect(This->ppPins[i]); IPin_Release(This->ppPins[i]); } CoTaskMemFree(This->ppPins); TRACE("Destroying transform filter\n"); This->csReceive.DebugInfo->Spare[0] = 0; DeleteCriticalSection(&This->csReceive); FreeMediaType(&This->pmt); QualityControlImpl_Destroy(This->qcimpl); IUnknown_Release(This->seekthru_unk); BaseFilter_Destroy(&This->filter); CoTaskMemFree(This); return 0; } else return refCount; }
ULONG WINAPI TransformFilterImpl_Release(IBaseFilter * iface) { TransformFilter *This = (TransformFilter *)iface; ULONG refCount = BaseFilterImpl_Release(iface); TRACE("(%p/%p)->() Release from %d\n", This, iface, refCount + 1); if (!refCount) { ULONG i; for (i = 0; i < This->npins; i++) { IPin *pConnectedTo; if (SUCCEEDED(IPin_ConnectedTo(This->ppPins[i], &pConnectedTo))) { IPin_Disconnect(pConnectedTo); IPin_Release(pConnectedTo); } IPin_Disconnect(This->ppPins[i]); IPin_Release(This->ppPins[i]); } CoTaskMemFree(This->ppPins); TRACE("Destroying transform filter\n"); FreeMediaType(&This->pmt); CoTaskMemFree(This); return 0; } else return refCount; }
/* This test doesn't use the quartz filtergraph because it makes it impossible * to be certain that a thread is really one owned by the avi splitter. * A lot of the decoder filters will also have their own thread, and Windows' * filtergraph has a separate thread for start/stop/seeking requests. * By avoiding the filtergraph altogether and connecting streams directly to * the null renderer I am sure that this is not the case here. */ static void test_threads(void) { IFileSourceFilter *pfile = NULL; IBaseFilter *preader = NULL, *pavi = NULL; IEnumPins *enumpins = NULL; IPin *filepin = NULL, *avipin = NULL; HRESULT hr; int baselevel, curlevel, expected; HANDLE file = NULL; PIN_DIRECTION dir = PINDIR_OUTPUT; char buffer[13]; DWORD readbytes; FILTER_STATE state; /* We need another way of counting threads on NT4. Skip these tests (for now) */ if (!pCreateToolhelp32Snapshot || !pThread32First || !pThread32Next) { win_skip("Needed thread functions are not available (NT4)\n"); return; } /* Before doing anything (the thread count at the start differs per OS) */ baselevel = count_threads(); file = CreateFileW(wfile, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); if (file == INVALID_HANDLE_VALUE) { skip("Could not read test file \"%s\", skipping test\n", afile); return; } memset(buffer, 0, 13); readbytes = 12; ReadFile(file, buffer, readbytes, &readbytes, NULL); CloseHandle(file); if (strncmp(buffer, "RIFF", 4) || strcmp(buffer + 8, "AVI ")) { skip("%s is not an avi riff file, not doing the avi splitter test\n", afile); return; } hr = IUnknown_QueryInterface(pAviSplitter, &IID_IFileSourceFilter, (void **)&pfile); ok(hr == E_NOINTERFACE, "Avi splitter returns unexpected error: %08x\n", hr); if (pfile) IFileSourceFilter_Release(pfile); pfile = NULL; hr = CoCreateInstance(&CLSID_AsyncReader, NULL, CLSCTX_INPROC_SERVER, &IID_IBaseFilter, (LPVOID*)&preader); ok(hr == S_OK, "Could not create asynchronous reader: %08x\n", hr); if (hr != S_OK) goto fail; hr = IBaseFilter_QueryInterface(preader, &IID_IFileSourceFilter, (void**)&pfile); ok(hr == S_OK, "Could not get IFileSourceFilter: %08x\n", hr); if (hr != S_OK) goto fail; hr = IUnknown_QueryInterface(pAviSplitter, &IID_IBaseFilter, (void**)&pavi); ok(hr == S_OK, "Could not get base filter: %08x\n", hr); if (hr != S_OK) goto fail; hr = IFileSourceFilter_Load(pfile, wfile, NULL); if (hr != S_OK) { trace("Could not load file\n"); goto fail; } hr = IBaseFilter_EnumPins(preader, &enumpins); ok(hr == S_OK, "No enumpins: %08x\n", hr); if (hr != S_OK) goto fail; hr = IEnumPins_Next(enumpins, 1, &filepin, NULL); ok(hr == S_OK, "No pin: %08x\n", hr); if (hr != S_OK) goto fail; IEnumPins_Release(enumpins); enumpins = NULL; hr = IBaseFilter_EnumPins(pavi, &enumpins); ok(hr == S_OK, "No enumpins: %08x\n", hr); if (hr != S_OK) goto fail; hr = IEnumPins_Next(enumpins, 1, &avipin, NULL); ok(hr == S_OK, "No pin: %08x\n", hr); if (hr != S_OK) goto fail; curlevel = count_threads(); ok(curlevel == baselevel, "The thread count should be %d not %d\n", baselevel, curlevel); hr = IPin_Connect(filepin, avipin, NULL); ok(hr == S_OK, "Could not connect: %08x\n", hr); if (hr != S_OK) goto fail; expected = 1 + baselevel; curlevel = count_threads(); ok(curlevel == expected, "The thread count should be %d not %d\n", expected, curlevel); IPin_Release(avipin); avipin = NULL; IEnumPins_Reset(enumpins); /* Windows puts the pins in the order: Outputpins - Inputpin, * wine does the reverse, just don't test it for now * Hate to admit it, but windows way makes more sense */ while (IEnumPins_Next(enumpins, 1, &avipin, NULL) == S_OK) { IPin_QueryDirection(avipin, &dir); if (dir == PINDIR_OUTPUT) { /* Well, connect it to a null renderer! */ IBaseFilter *pnull = NULL; IEnumPins *nullenum = NULL; IPin *nullpin = NULL; hr = CoCreateInstance(&CLSID_NullRenderer, NULL, CLSCTX_INPROC_SERVER, &IID_IBaseFilter, (LPVOID*)&pnull); ok(hr == S_OK, "Could not create null renderer: %08x\n", hr); if (hr != S_OK) break; IBaseFilter_EnumPins(pnull, &nullenum); IEnumPins_Next(nullenum, 1, &nullpin, NULL); IEnumPins_Release(nullenum); IPin_QueryDirection(nullpin, &dir); hr = IPin_Connect(avipin, nullpin, NULL); ok(hr == S_OK, "Failed to connect output pin: %08x\n", hr); IPin_Release(nullpin); if (hr != S_OK) { IBaseFilter_Release(pnull); break; } IBaseFilter_Run(pnull, 0); ++expected; } IPin_Release(avipin); avipin = NULL; } if (avipin) IPin_Release(avipin); avipin = NULL; if (hr != S_OK) goto fail2; /* At this point there is a minimalistic connected avi splitter that can * be used for all sorts of source filter tests. However that still needs * to be written at a later time. * * Interesting tests: * - Can you disconnect an output pin while running? * Expecting: Yes * - Can you disconnect the pullpin while running? * Expecting: No * - Is the reference count incremented during playback or when connected? * Does this happen once for every output pin? Or is there something else * going on. * Expecting: You tell me */ IBaseFilter_Run(preader, 0); IBaseFilter_Run(pavi, 0); IBaseFilter_GetState(pavi, INFINITE, &state); curlevel = count_threads(); ok(curlevel == expected, "The thread count should be %d not %d\n", expected, curlevel); IBaseFilter_Pause(pavi); IBaseFilter_Pause(preader); IBaseFilter_Stop(pavi); IBaseFilter_Stop(preader); IBaseFilter_GetState(pavi, INFINITE, &state); IBaseFilter_GetState(preader, INFINITE, &state); fail2: IEnumPins_Reset(enumpins); while (IEnumPins_Next(enumpins, 1, &avipin, NULL) == S_OK) { IPin *to = NULL; IPin_QueryDirection(avipin, &dir); IPin_ConnectedTo(avipin, &to); if (to) { IPin_Release(to); if (dir == PINDIR_OUTPUT) { PIN_INFO info; IPin_QueryPinInfo(to, &info); /* Release twice: Once normal, second from the * previous while loop */ IBaseFilter_Stop(info.pFilter); IPin_Disconnect(to); IPin_Disconnect(avipin); IBaseFilter_Release(info.pFilter); IBaseFilter_Release(info.pFilter); } else { IPin_Disconnect(to); IPin_Disconnect(avipin); } } IPin_Release(avipin); avipin = NULL; } fail: if (hr != S_OK) skip("Prerequisites not matched, skipping remainder of test\n"); if (enumpins) IEnumPins_Release(enumpins); if (avipin) IPin_Release(avipin); if (filepin) { IPin *to = NULL; IPin_ConnectedTo(filepin, &to); if (to) { IPin_Disconnect(filepin); IPin_Disconnect(to); } IPin_Release(filepin); } if (preader) IBaseFilter_Release(preader); if (pavi) IBaseFilter_Release(pavi); if (pfile) IFileSourceFilter_Release(pfile); curlevel = count_threads(); todo_wine ok(curlevel == baselevel, "The thread count should be %d not %d\n", baselevel, curlevel); }
static void test_AviMux(void) { test_filter source_filter, sink_filter; VIDEOINFOHEADER videoinfoheader; IPin *avimux_in, *avimux_out, *pin; AM_MEDIA_TYPE source_media_type; AM_MEDIA_TYPE *media_type; PIN_DIRECTION dir; IBaseFilter *avimux; IEnumPins *ep; IEnumMediaTypes *emt; HRESULT hr; init_test_filter(&source_filter, PINDIR_OUTPUT, SOURCE_FILTER); init_test_filter(&sink_filter, PINDIR_INPUT, SINK_FILTER); hr = CoCreateInstance(&CLSID_AviDest, NULL, CLSCTX_INPROC_SERVER, &IID_IBaseFilter, (void**)&avimux); ok(hr == S_OK || broken(hr == REGDB_E_CLASSNOTREG), "couldn't create AVI Mux filter, hr = %08x\n", hr); if(hr != S_OK) { win_skip("AVI Mux filter is not registered\n"); return; } hr = IBaseFilter_EnumPins(avimux, &ep); ok(hr == S_OK, "EnumPins returned %x\n", hr); hr = IEnumPins_Next(ep, 1, &avimux_out, NULL); ok(hr == S_OK, "Next returned %x\n", hr); hr = IPin_QueryDirection(avimux_out, &dir); ok(hr == S_OK, "QueryDirection returned %x\n", hr); ok(dir == PINDIR_OUTPUT, "dir = %d\n", dir); hr = IEnumPins_Next(ep, 1, &avimux_in, NULL); ok(hr == S_OK, "Next returned %x\n", hr); hr = IPin_QueryDirection(avimux_in, &dir); ok(hr == S_OK, "QueryDirection returned %x\n", hr); ok(dir == PINDIR_INPUT, "dir = %d\n", dir); IEnumPins_Release(ep); hr = IPin_EnumMediaTypes(avimux_out, &emt); ok(hr == S_OK, "EnumMediaTypes returned %x\n", hr); hr = IEnumMediaTypes_Next(emt, 1, &media_type, NULL); ok(hr == S_OK, "Next returned %x\n", hr); ok(IsEqualIID(&media_type->majortype, &MEDIATYPE_Stream), "majortype = %s\n", debugstr_guid(&media_type->majortype)); ok(IsEqualIID(&media_type->subtype, &MEDIASUBTYPE_Avi), "subtype = %s\n", debugstr_guid(&media_type->subtype)); ok(media_type->bFixedSizeSamples, "bFixedSizeSamples = %x\n", media_type->bFixedSizeSamples); ok(!media_type->bTemporalCompression, "bTemporalCompression = %x\n", media_type->bTemporalCompression); ok(media_type->lSampleSize == 1, "lSampleSize = %d\n", media_type->lSampleSize); ok(IsEqualIID(&media_type->formattype, &GUID_NULL), "formattype = %s\n", debugstr_guid(&media_type->formattype)); ok(!media_type->pUnk, "pUnk = %p\n", media_type->pUnk); ok(!media_type->cbFormat, "cbFormat = %d\n", media_type->cbFormat); ok(!media_type->pbFormat, "pbFormat = %p\n", media_type->pbFormat); CoTaskMemFree(media_type); hr = IEnumMediaTypes_Next(emt, 1, &media_type, NULL); ok(hr == S_FALSE, "Next returned %x\n", hr); IEnumMediaTypes_Release(emt); hr = IPin_EnumMediaTypes(avimux_in, &emt); ok(hr == S_OK, "EnumMediaTypes returned %x\n", hr); hr = IEnumMediaTypes_Reset(emt); ok(hr == S_OK, "Reset returned %x\n", hr); hr = IEnumMediaTypes_Next(emt, 1, &media_type, NULL); ok(hr == S_FALSE, "Next returned %x\n", hr); IEnumMediaTypes_Release(emt); hr = IPin_ReceiveConnection(avimux_in, &source_filter.IPin_iface, NULL); ok(hr == E_POINTER, "ReceiveConnection returned %x\n", hr); current_calls_list = NULL; memset(&source_media_type, 0, sizeof(AM_MEDIA_TYPE)); memset(&videoinfoheader, 0, sizeof(VIDEOINFOHEADER)); source_media_type.majortype = MEDIATYPE_Video; source_media_type.subtype = MEDIASUBTYPE_RGB32; source_media_type.formattype = FORMAT_VideoInfo; source_media_type.bFixedSizeSamples = TRUE; source_media_type.lSampleSize = 40000; source_media_type.cbFormat = sizeof(VIDEOINFOHEADER); source_media_type.pbFormat = (BYTE*)&videoinfoheader; videoinfoheader.AvgTimePerFrame = 333333; videoinfoheader.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); videoinfoheader.bmiHeader.biWidth = 100; videoinfoheader.bmiHeader.biHeight = 100; videoinfoheader.bmiHeader.biPlanes = 1; videoinfoheader.bmiHeader.biBitCount = 32; videoinfoheader.bmiHeader.biSizeImage = 40000; videoinfoheader.bmiHeader.biClrImportant = 256; hr = IPin_ReceiveConnection(avimux_in, &source_filter.IPin_iface, &source_media_type); ok(hr == S_OK, "ReceiveConnection returned %x\n", hr); hr = IPin_ConnectedTo(avimux_in, &pin); ok(hr == S_OK, "ConnectedTo returned %x\n", hr); ok(pin == &source_filter.IPin_iface, "incorrect pin: %p, expected %p\n", pin, &source_filter.IPin_iface); hr = IPin_Connect(avimux_out, &source_filter.IPin_iface, NULL); todo_wine ok(hr == VFW_E_INVALID_DIRECTION, "Connect returned %x\n", hr); hr = IBaseFilter_JoinFilterGraph(avimux, (IFilterGraph*)&GraphBuilder, NULL); ok(hr == S_OK, "JoinFilterGraph returned %x\n", hr); SET_EXPECT(ReceiveConnection); SET_EXPECT(GetAllocatorRequirements); SET_EXPECT(NotifyAllocator); SET_EXPECT(Reconnect); hr = IPin_Connect(avimux_out, &sink_filter.IPin_iface, NULL); ok(hr == S_OK, "Connect returned %x\n", hr); CHECK_CALLED(ReceiveConnection); CHECK_CALLED(GetAllocatorRequirements); CHECK_CALLED(NotifyAllocator); CHECK_CALLED(Reconnect); hr = IPin_Disconnect(avimux_out); ok(hr == S_OK, "Disconnect returned %x\n", hr); IPin_Release(avimux_in); IPin_Release(avimux_out); IBaseFilter_Release(avimux); }