[QUARTZ_WINETEST]
[reactos.git] / rostests / winetests / quartz / avisplitter.c
1 /*
2 * Unit tests for the avi splitter functions
3 *
4 * Copyright (C) 2007 Google (Lei Zhang)
5 * Copyright (C) 2008 Google (Maarten Lankhorst)
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 "wine/test.h"
25 #include "dshow.h"
26 #include "tlhelp32.h"
27
28 static HANDLE (WINAPI *pCreateToolhelp32Snapshot)(DWORD, DWORD);
29 static BOOL (WINAPI *pThread32First)(HANDLE, LPTHREADENTRY32);
30 static BOOL (WINAPI *pThread32Next)(HANDLE, LPTHREADENTRY32);
31
32 static IUnknown *pAviSplitter = NULL;
33
34 static int count_threads(void)
35 {
36 THREADENTRY32 te;
37 int threads;
38 HANDLE h;
39
40 h = pCreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
41 te.dwSize = sizeof(te);
42
43 if (h == INVALID_HANDLE_VALUE)
44 return -1;
45
46 pThread32First(h, &te);
47 if (te.th32OwnerProcessID == GetCurrentProcessId())
48 threads = 1;
49 else
50 threads = 0;
51
52 while (pThread32Next(h, &te))
53 if (te.th32OwnerProcessID == GetCurrentProcessId())
54 ++threads;
55
56 CloseHandle(h);
57 return threads;
58 }
59
60 static BOOL create_avisplitter(void)
61 {
62 HRESULT hr;
63
64 hr = CoCreateInstance(&CLSID_AviSplitter, NULL, CLSCTX_INPROC_SERVER,
65 &IID_IUnknown, (LPVOID*)&pAviSplitter);
66 return (hr == S_OK && pAviSplitter != NULL);
67 }
68
69 static void release_avisplitter(void)
70 {
71 HRESULT hr;
72
73 Sleep(1000);
74 hr = IUnknown_Release(pAviSplitter);
75
76 /* Looks like wine has a reference leak somewhere on test_threads tests,
77 * it passes in windows
78 */
79 ok(hr == 0, "IUnknown_Release failed with %d\n", (INT)hr);
80
81 while (hr > 0)
82 hr = IUnknown_Release(pAviSplitter);
83 pAviSplitter = NULL;
84 }
85
86 static void test_query_interface(void)
87 {
88 HRESULT hr;
89 ULONG ref;
90 IUnknown *iface= NULL;
91
92 #define TEST_INTERFACE(riid,expected) do { \
93 hr = IUnknown_QueryInterface(pAviSplitter, &riid, (void**)&iface); \
94 ok( hr == expected, #riid" should %s got %08X\n", expected==S_OK ? "exist" : "not be present", GetLastError() ); \
95 if (hr == S_OK) { \
96 ref = IUnknown_Release(iface); \
97 ok(ref == 1, "Reference is %u, expected 1\n", ref); \
98 } \
99 iface = NULL; \
100 } while(0)
101
102 TEST_INTERFACE(IID_IBaseFilter,S_OK);
103 TEST_INTERFACE(IID_IMediaSeeking,E_NOINTERFACE);
104 TEST_INTERFACE(IID_IKsPropertySet,E_NOINTERFACE);
105 TEST_INTERFACE(IID_IMediaPosition,E_NOINTERFACE);
106 TEST_INTERFACE(IID_IQualityControl,E_NOINTERFACE);
107 TEST_INTERFACE(IID_IQualProp,E_NOINTERFACE);
108 #undef TEST_INTERFACE
109 }
110
111 static void test_pin(IPin *pin)
112 {
113 IMemInputPin *mpin = NULL;
114
115 IPin_QueryInterface(pin, &IID_IMemInputPin, (void **)&mpin);
116
117 ok(mpin == NULL, "IMemInputPin found!\n");
118 if (mpin)
119 IMemInputPin_Release(mpin);
120 /* TODO */
121 }
122
123 static void test_basefilter(void)
124 {
125 IEnumPins *pin_enum = NULL;
126 IBaseFilter *base = NULL;
127 IPin *pins[2];
128 ULONG ref;
129 HRESULT hr;
130
131 IUnknown_QueryInterface(pAviSplitter, &IID_IBaseFilter, (void **)&base);
132 if (base == NULL)
133 {
134 /* test_query_interface handles this case */
135 skip("No IBaseFilter\n");
136 return;
137 }
138
139 hr = IBaseFilter_EnumPins(base, NULL);
140 ok(hr == E_POINTER, "hr = %08x and not E_POINTER\n", hr);
141
142 hr= IBaseFilter_EnumPins(base, &pin_enum);
143 ok(hr == S_OK, "hr = %08x and not S_OK\n", hr);
144
145 hr = IEnumPins_Next(pin_enum, 1, NULL, NULL);
146 ok(hr == E_POINTER, "hr = %08x and not E_POINTER\n", hr);
147
148 hr = IEnumPins_Next(pin_enum, 2, pins, NULL);
149 ok(hr == E_INVALIDARG, "hr = %08x and not E_INVALIDARG\n", hr);
150
151 pins[0] = (void *)0xdead;
152 pins[1] = (void *)0xdeed;
153
154 hr = IEnumPins_Next(pin_enum, 2, pins, &ref);
155 ok(hr == S_FALSE, "hr = %08x instead of S_FALSE\n", hr);
156 ok(pins[0] != (void *)0xdead && pins[0] != NULL,
157 "pins[0] = %p\n", pins[0]);
158 if (pins[0] != (void *)0xdead && pins[0] != NULL)
159 {
160 test_pin(pins[0]);
161 IPin_Release(pins[0]);
162 }
163
164 ok(pins[1] == (void *)0xdeed, "pins[1] = %p\n", pins[1]);
165
166 ref = IEnumPins_Release(pin_enum);
167 ok(ref == 0, "ref is %u and not 0!\n", ref);
168
169 IBaseFilter_Release(base);
170 }
171
172 static const WCHAR wfile[] = {'t','e','s','t','.','a','v','i',0};
173 static const char afile[] = "test.avi";
174
175 /* This test doesn't use the quartz filtergraph because it makes it impossible
176 * to be certain that a thread is really one owned by the avi splitter.
177 * A lot of the decoder filters will also have their own thread, and Windows'
178 * filtergraph has a separate thread for start/stop/seeking requests.
179 * By avoiding the filtergraph altogether and connecting streams directly to
180 * the null renderer I am sure that this is not the case here.
181 */
182 static void test_threads(void)
183 {
184 IFileSourceFilter *pfile = NULL;
185 IBaseFilter *preader = NULL, *pavi = NULL;
186 IEnumPins *enumpins = NULL;
187 IPin *filepin = NULL, *avipin = NULL;
188 HRESULT hr;
189 int baselevel, curlevel, expected;
190 HANDLE file = NULL;
191 PIN_DIRECTION dir = PINDIR_OUTPUT;
192 char buffer[13];
193 DWORD readbytes;
194 FILTER_STATE state;
195
196 /* We need another way of counting threads on NT4. Skip these tests (for now) */
197 if (!pCreateToolhelp32Snapshot || !pThread32First || !pThread32Next)
198 {
199 win_skip("Needed thread functions are not available (NT4)\n");
200 return;
201 }
202
203 /* Before doing anything (the thread count at the start differs per OS) */
204 baselevel = count_threads();
205
206 file = CreateFileW(wfile, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE,
207 NULL, OPEN_EXISTING, 0, NULL);
208 if (file == INVALID_HANDLE_VALUE)
209 {
210 skip("Could not read test file \"%s\", skipping test\n", afile);
211 return;
212 }
213
214 memset(buffer, 0, 13);
215 readbytes = 12;
216 ReadFile(file, buffer, readbytes, &readbytes, NULL);
217 CloseHandle(file);
218 if (strncmp(buffer, "RIFF", 4) || strcmp(buffer + 8, "AVI "))
219 {
220 skip("%s is not an avi riff file, not doing the avi splitter test\n",
221 afile);
222 return;
223 }
224
225 hr = IUnknown_QueryInterface(pAviSplitter, &IID_IFileSourceFilter,
226 (void **)&pfile);
227 ok(hr == E_NOINTERFACE,
228 "Avi splitter returns unexpected error: %08x\n", hr);
229 if (pfile)
230 IFileSourceFilter_Release(pfile);
231 pfile = NULL;
232
233 hr = CoCreateInstance(&CLSID_AsyncReader, NULL, CLSCTX_INPROC_SERVER,
234 &IID_IBaseFilter, (LPVOID*)&preader);
235 ok(hr == S_OK, "Could not create asynchronous reader: %08x\n", hr);
236 if (hr != S_OK)
237 goto fail;
238
239 hr = IBaseFilter_QueryInterface(preader, &IID_IFileSourceFilter,
240 (void**)&pfile);
241 ok(hr == S_OK, "Could not get IFileSourceFilter: %08x\n", hr);
242 if (hr != S_OK)
243 goto fail;
244
245 hr = IUnknown_QueryInterface(pAviSplitter, &IID_IBaseFilter,
246 (void**)&pavi);
247 ok(hr == S_OK, "Could not get base filter: %08x\n", hr);
248 if (hr != S_OK)
249 goto fail;
250
251 hr = IFileSourceFilter_Load(pfile, wfile, NULL);
252 if (hr != S_OK)
253 {
254 trace("Could not load file\n");
255 goto fail;
256 }
257
258 hr = IBaseFilter_EnumPins(preader, &enumpins);
259 ok(hr == S_OK, "No enumpins: %08x\n", hr);
260 if (hr != S_OK)
261 goto fail;
262
263 hr = IEnumPins_Next(enumpins, 1, &filepin, NULL);
264 ok(hr == S_OK, "No pin: %08x\n", hr);
265 if (hr != S_OK)
266 goto fail;
267
268 IEnumPins_Release(enumpins);
269 enumpins = NULL;
270
271 hr = IBaseFilter_EnumPins(pavi, &enumpins);
272 ok(hr == S_OK, "No enumpins: %08x\n", hr);
273 if (hr != S_OK)
274 goto fail;
275
276 hr = IEnumPins_Next(enumpins, 1, &avipin, NULL);
277 ok(hr == S_OK, "No pin: %08x\n", hr);
278 if (hr != S_OK)
279 goto fail;
280
281 curlevel = count_threads();
282 ok(curlevel == baselevel,
283 "The thread count should be %d not %d\n", baselevel, curlevel);
284
285 hr = IPin_Connect(filepin, avipin, NULL);
286 ok(hr == S_OK, "Could not connect: %08x\n", hr);
287 if (hr != S_OK)
288 goto fail;
289
290 expected = 1 + baselevel;
291 curlevel = count_threads();
292 ok(curlevel == expected,
293 "The thread count should be %d not %d\n", expected, curlevel);
294
295 IPin_Release(avipin);
296 avipin = NULL;
297
298 IEnumPins_Reset(enumpins);
299
300 /* Windows puts the pins in the order: Outputpins - Inputpin,
301 * wine does the reverse, just don't test it for now
302 * Hate to admit it, but windows way makes more sense
303 */
304 while (IEnumPins_Next(enumpins, 1, &avipin, NULL) == S_OK)
305 {
306 IPin_QueryDirection(avipin, &dir);
307 if (dir == PINDIR_OUTPUT)
308 {
309 /* Well, connect it to a null renderer! */
310 IBaseFilter *pnull = NULL;
311 IEnumPins *nullenum = NULL;
312 IPin *nullpin = NULL;
313
314 hr = CoCreateInstance(&CLSID_NullRenderer, NULL,
315 CLSCTX_INPROC_SERVER, &IID_IBaseFilter, (LPVOID*)&pnull);
316 ok(hr == S_OK, "Could not create null renderer: %08x\n", hr);
317 if (hr != S_OK)
318 break;
319
320 IBaseFilter_EnumPins(pnull, &nullenum);
321 IEnumPins_Next(nullenum, 1, &nullpin, NULL);
322 IEnumPins_Release(nullenum);
323 IPin_QueryDirection(nullpin, &dir);
324
325 hr = IPin_Connect(avipin, nullpin, NULL);
326 ok(hr == S_OK, "Failed to connect output pin: %08x\n", hr);
327 IPin_Release(nullpin);
328 if (hr != S_OK)
329 {
330 IBaseFilter_Release(pnull);
331 break;
332 }
333 IBaseFilter_Run(pnull, 0);
334 ++expected;
335 }
336
337 IPin_Release(avipin);
338 avipin = NULL;
339 }
340
341 if (avipin)
342 IPin_Release(avipin);
343 avipin = NULL;
344
345 if (hr != S_OK)
346 goto fail2;
347 /* At this point there is a minimalistic connected avi splitter that can
348 * be used for all sorts of source filter tests. However that still needs
349 * to be written at a later time.
350 *
351 * Interesting tests:
352 * - Can you disconnect an output pin while running?
353 * Expecting: Yes
354 * - Can you disconnect the pullpin while running?
355 * Expecting: No
356 * - Is the reference count incremented during playback or when connected?
357 * Does this happen once for every output pin? Or is there something else
358 * going on.
359 * Expecting: You tell me
360 */
361
362 IBaseFilter_Run(preader, 0);
363 IBaseFilter_Run(pavi, 0);
364 IBaseFilter_GetState(pavi, INFINITE, &state);
365
366 curlevel = count_threads();
367 ok(curlevel == expected,
368 "The thread count should be %d not %d\n", expected, curlevel);
369
370 IBaseFilter_Pause(pavi);
371 IBaseFilter_Pause(preader);
372 IBaseFilter_Stop(pavi);
373 IBaseFilter_Stop(preader);
374 IBaseFilter_GetState(pavi, INFINITE, &state);
375 IBaseFilter_GetState(preader, INFINITE, &state);
376
377 fail2:
378 IEnumPins_Reset(enumpins);
379 while (IEnumPins_Next(enumpins, 1, &avipin, NULL) == S_OK)
380 {
381 IPin *to = NULL;
382
383 IPin_QueryDirection(avipin, &dir);
384 IPin_ConnectedTo(avipin, &to);
385 if (to)
386 {
387 IPin_Release(to);
388
389 if (dir == PINDIR_OUTPUT)
390 {
391 PIN_INFO info;
392 IPin_QueryPinInfo(to, &info);
393
394 /* Release twice: Once normal, second from the
395 * previous while loop
396 */
397 IBaseFilter_Stop(info.pFilter);
398 IPin_Disconnect(to);
399 IPin_Disconnect(avipin);
400 IBaseFilter_Release(info.pFilter);
401 IBaseFilter_Release(info.pFilter);
402 }
403 else
404 {
405 IPin_Disconnect(to);
406 IPin_Disconnect(avipin);
407 }
408 }
409 IPin_Release(avipin);
410 avipin = NULL;
411 }
412
413 fail:
414 if (hr != S_OK)
415 skip("Prerequisites not matched, skipping remainder of test\n");
416 if (enumpins)
417 IEnumPins_Release(enumpins);
418
419 if (avipin)
420 IPin_Release(avipin);
421 if (filepin)
422 {
423 IPin *to = NULL;
424
425 IPin_ConnectedTo(filepin, &to);
426 if (to)
427 {
428 IPin_Disconnect(filepin);
429 IPin_Disconnect(to);
430 }
431 IPin_Release(filepin);
432 }
433
434 if (preader)
435 IBaseFilter_Release(preader);
436 if (pavi)
437 IBaseFilter_Release(pavi);
438 if (pfile)
439 IFileSourceFilter_Release(pfile);
440
441 curlevel = count_threads();
442 todo_wine
443 ok(curlevel == baselevel,
444 "The thread count should be %d not %d\n", baselevel, curlevel);
445 }
446
447 START_TEST(avisplitter)
448 {
449 HMODULE kernel32 = GetModuleHandleA("kernel32.dll");
450
451 pCreateToolhelp32Snapshot = (void*)GetProcAddress(kernel32, "CreateToolhelp32Snapshot");
452 pThread32First = (void*)GetProcAddress(kernel32, "Thread32First");
453 pThread32Next = (void*)GetProcAddress(kernel32, "Thread32Next");
454
455 CoInitialize(NULL);
456
457 if (!create_avisplitter())
458 {
459 skip("Could not create avisplitter\n");
460 return;
461 }
462
463 test_query_interface();
464 test_basefilter();
465 test_threads();
466
467 release_avisplitter();
468
469 CoUninitialize();
470 }