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 HRESULT WINAPI Parser_PullPin_Disconnect(IPin * iface) { HRESULT hr; PullPin *This = impl_PullPin_from_IPin(iface); TRACE("()\n"); EnterCriticalSection(&This->thread_lock); EnterCriticalSection(This->pin.pCritSec); { if (This->pin.pConnectedTo) { FILTER_STATE state; ParserImpl *Parser = impl_from_IBaseFilter(This->pin.pinInfo.pFilter); LeaveCriticalSection(This->pin.pCritSec); hr = IBaseFilter_GetState(This->pin.pinInfo.pFilter, INFINITE, &state); EnterCriticalSection(This->pin.pCritSec); if (SUCCEEDED(hr) && (state == State_Stopped) && SUCCEEDED(Parser->fnDisconnect(Parser))) { LeaveCriticalSection(This->pin.pCritSec); PullPin_Disconnect(iface); EnterCriticalSection(This->pin.pCritSec); hr = Parser_RemoveOutputPins(impl_from_IBaseFilter(This->pin.pinInfo.pFilter)); } else hr = VFW_E_NOT_STOPPED; } else hr = S_FALSE; } LeaveCriticalSection(This->pin.pCritSec); LeaveCriticalSection(&This->thread_lock); return hr; }
/* 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); }