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