1 /*
2  * Unit test suite for AVI Functions
3  *
4  * Copyright 2008 Detlef Riekenberg
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  */
21 
22 #define COBJMACROS
23 #define CONST_VTABLE
24 
25 #include "wine/test.h"
26 #include "initguid.h"
27 #include "wingdi.h"
28 #include "vfw.h"
29 
30 /* ########################### */
31 
32 DEFINE_AVIGUID(CLSID_WAVFile,   0x00020003, 0, 0);
33 static const CHAR winetest0[] = "winetest0";
34 static const CHAR winetest1[] = "winetest1";
35 static const CHAR testfilename[]  = "wine_avifil32_test.avi";
36 
37 /* ########################### */
38 
39 static const DWORD deffh[] = /* file_header */
40 {
41     FOURCC_RIFF, 0x34c6 /* length */, formtypeAVI,
42     FOURCC_LIST, 0x1ac /* length */,
43     listtypeAVIHEADER, ckidAVIMAINHDR, sizeof(MainAVIHeader),
44 };
45 
46 static const MainAVIHeader defmah =
47 {
48     0x00008256, /* dwMicroSecPerFrame   */
49     0x000080e8, /* dwMaxBytesPerSec     */
50     0x00000000, /* dwPaddingGranularity */
51     0x00000910, /* dwFlags              */
52     1,          /* dwTotalFrames        */
53     0,          /* dwInitialFrames      */
54     2,          /* dwStreams            */
55     0x00100000, /* dwSuggestedBufferSize*/
56     8,          /* dwWidth              */
57     6,          /* dwHeight             */
58     { 0, 0, 0, 0 } /* dwReserved[4] */
59 };
60 
61 static const AVIStreamHeader defash0 =
62 {
63     streamtypeVIDEO, /* fccType              */
64     0x30323449,      /* fccHandler           */
65     0x00000000,      /* dwFlags              */
66     0,               /* wPriority            */
67     0,               /* wLanguage            */
68     0,               /* dwInitialFrames      */
69     0x000003e9,      /* dwScale              */
70     0x00007530,      /* dwRate               */
71     0,               /* dwStart              */
72     1,               /* dwLength             */
73     0x00100000,      /* dwSuggestedBufferSize*/
74     0xffffffff,      /* dwQuality            */
75     0,               /* dwSampleSize         */
76     { 0, 0, 0, 0 }   /* short left right top bottom */
77 };
78 
79 static const AVIStreamHeader defash1 =
80 {
81     /* AVIStreamHeader */
82     streamtypeAUDIO, /* fccType              */
83     1,               /* fccHandler           */
84     0,               /* dwFlags              */
85     0,               /* wPriority            */
86     0,               /* wLanguage            */
87     0,               /* dwInitialFrames      */
88     1,               /* dwScale              */
89     0x00002b11,      /* dwRate               */
90     0,               /* dwStart              */
91     0x00000665,      /* dwLength             */
92     0x00003000,      /* dwSuggestedBufferSize*/
93     0xffffffff,      /* dwQuality            */
94     2,               /* dwSampleSize         */
95     { 0, 0, 0, 0 }   /* short left right top bottom */
96 };
97 
98 static const PCMWAVEFORMAT defpcmwf =
99 {
100     {
101         1,      /* wFormatTag      */
102         2,      /* nChannels       */
103         11025,  /* nSamplesPerSec  */
104         22050,  /* nAvgBytesPerSec */
105         2,      /* nBlockAlign     */
106     },
107     8,      /* wBitsPerSample  */
108 };
109 
110 typedef struct common_avi_headers {
111     DWORD           fh[sizeof(deffh)];
112     MainAVIHeader   mah;
113     AVIStreamHeader ash0;
114     AVIStreamHeader ash1;
115     PCMWAVEFORMAT   pcmwf;
116 } COMMON_AVI_HEADERS;
117 
118 /* Extra data needed to get the VFW API to load the file */
119 /* DWORD deffh */
120 /* MainAVIHeader mah */
121 static const DWORD streamlist[] =
122 {
123     FOURCC_LIST, 0xd4 /* length */,
124     listtypeSTREAMHEADER, ckidSTREAMHEADER, 0x38 /* length */,
125 };
126 /* AVIStreamHeader ash0 */
127 static const DWORD videostreamformat[] =
128 {
129     ckidSTREAMFORMAT, 0x28 /* length */,
130     0x00000028, 0x00000008, 0x00000006, 0x00180001,
131     0x30323449, 0x00000090, 0x00000000, 0x00000000,
132     0x00000000, 0x00000000,
133 };
134 static const DWORD padding1[] =
135 {
136     ckidAVIPADDING, 0xc /* length */,
137     0x00000004, 0x00000000, 0x63643030
138 };
139 static const DWORD videopropheader[] =
140 {
141     0x70727076, 0x44 /* length */,
142     0x00000000, 0x00000000,
143     0x0000001e, 0x00000008, 0x00000006, 0x00100009,
144     0x00000008, 0x00000006, 0x00000001, 0x00000006,
145     0x00000008, 0x00000006, 0x00000008, 0x00000000,
146     0x00000000, 0x00000000, 0x00000000,
147     FOURCC_LIST, 0x70 /* length */,
148     listtypeSTREAMHEADER, ckidSTREAMHEADER, 0x38 /* length */,
149 };
150 /* AVIStreamHeader ash1 */
151 static const DWORD audiostreamformat_pre[] =
152 {
153     ckidSTREAMFORMAT, sizeof(PCMWAVEFORMAT) /* length */,
154 };
155 /* PCMWAVEFORMAT pcmwf */
156 static DWORD data[] =
157 {
158     ckidAVIPADDING, 0xc /* length */,
159     0x00000004, 0x00000000, 0x62773130,
160     ckidAVIPADDING, 0xc /* length */,
161     0x6c6d646f, 0x686c6d64, 0x000000f8,
162     FOURCC_LIST, 0x18 /* length */,
163     0x4f464e49,
164     0x54465349, 0xc /* length */,
165     0x6676614c, 0x332e3235, 0x00302e37,
166     ckidAVIPADDING, 0x4 /* length */,
167     0,
168     FOURCC_LIST, 0xd1b /* length */, listtypeAVIMOVIE,
169     0, 0
170 };
171 
172 /* ########################### */
173 
174 static void test_AVISaveOptions(void)
175 {
176     AVICOMPRESSOPTIONS options[2];
177     LPAVICOMPRESSOPTIONS poptions[2];
178     PAVISTREAM streams[2] = {NULL, NULL};
179     HRESULT hres;
180     DWORD   res;
181     LONG    lres;
182 
183     poptions[0] = &options[0];
184     poptions[1] = &options[1];
185     ZeroMemory(options, sizeof(options));
186 
187     SetLastError(0xdeadbeef);
188     hres = CreateEditableStream(&streams[0], NULL);
189     ok(hres == AVIERR_OK, "0: got 0x%x and %p (expected AVIERR_OK)\n", hres, streams[0]);
190 
191     SetLastError(0xdeadbeef);
192     hres = CreateEditableStream(&streams[1], NULL);
193     ok(hres == AVIERR_OK, "1: got 0x%x and %p (expected AVIERR_OK)\n", hres, streams[1]);
194 
195     SetLastError(0xdeadbeef);
196     hres = EditStreamSetNameA(streams[0], winetest0);
197     ok(hres == AVIERR_OK, "0: got 0x%x (expected AVIERR_OK)\n", hres);
198 
199     SetLastError(0xdeadbeef);
200     hres = EditStreamSetNameA(streams[1], winetest1);
201     ok(hres == AVIERR_OK, "1: got 0x%x (expected AVIERR_OK)\n", hres);
202 
203     if (winetest_interactive) {
204         SetLastError(0xdeadbeef);
205         res = AVISaveOptions(0, ICMF_CHOOSE_DATARATE |ICMF_CHOOSE_KEYFRAME | ICMF_CHOOSE_ALLCOMPRESSORS,
206                              2, streams, poptions);
207         trace("got %u with 0x%x/%u\n", res, GetLastError(), GetLastError());
208     }
209 
210     SetLastError(0xdeadbeef);
211     lres = AVISaveOptionsFree(2, poptions);
212     ok(lres == AVIERR_OK, "got 0x%x with 0x%x/%u\n", lres, GetLastError(), GetLastError());
213 
214     SetLastError(0xdeadbeef);
215     res = AVIStreamRelease(streams[0]);
216     ok(res == 0, "0: got refcount %u (expected 0)\n", res);
217 
218     SetLastError(0xdeadbeef);
219     res = AVIStreamRelease(streams[1]);
220     ok(res == 0, "1: got refcount %u (expected 0)\n", res);
221 
222 }
223 
224 /* ########################### */
225 
226 static void test_EditStreamSetInfo(void)
227 {
228     PAVISTREAM stream = NULL;
229     HRESULT hres;
230     AVISTREAMINFOA info, info2;
231 
232     hres = CreateEditableStream(&stream, NULL);
233     ok(hres == AVIERR_OK, "got 0x%08X, expected AVIERR_OK\n", hres);
234 
235     /* Size parameter is somehow checked (notice the crash with size=-1 below) */
236     hres = EditStreamSetInfoA(stream, NULL, 0);
237     ok( hres == AVIERR_BADSIZE, "got 0x%08X, expected AVIERR_BADSIZE\n", hres);
238 
239     hres = EditStreamSetInfoA(stream, NULL, sizeof(AVISTREAMINFOA)-1 );
240     ok( hres == AVIERR_BADSIZE, "got 0x%08X, expected AVIERR_BADSIZE\n", hres);
241 
242     if(0)
243     {
244         /* Crashing - first parameter not checked */
245         EditStreamSetInfoA(NULL, &info, sizeof(info) );
246 
247         /* Crashing - second parameter not checked */
248         EditStreamSetInfoA(stream, NULL, sizeof(AVISTREAMINFOA) );
249 
250         EditStreamSetInfoA(stream, NULL, -1);
251     }
252 
253     hres = AVIStreamInfoA(stream, &info, sizeof(info) );
254     ok( hres == 0, "got 0x%08X, expected 0\n", hres);
255 
256              /* Does the function check what's it's updating ? */
257 
258 #define IS_INFO_UPDATED(m) do { \
259     hres = EditStreamSetInfoA(stream, &info, sizeof(info) ); \
260     ok( hres == 0, "got 0x%08X, expected 0\n", hres); \
261     hres = AVIStreamInfoA(stream, &info2, sizeof(info2) ); \
262     ok( hres == 0, "got 0x%08X, expected 0\n", hres); \
263     ok( info2.m == info.m, "EditStreamSetInfo did not update "#m" parameter\n" ); \
264     } while(0)
265 
266     info.dwStart++;
267     IS_INFO_UPDATED(dwStart);
268     info.dwStart = 0;
269     IS_INFO_UPDATED(dwStart);
270 
271     info.wPriority++;
272     IS_INFO_UPDATED(wPriority);
273     info.wPriority = 0;
274     IS_INFO_UPDATED(wPriority);
275 
276     info.wLanguage++;
277     IS_INFO_UPDATED(wLanguage);
278     info.wLanguage = 0;
279     IS_INFO_UPDATED(wLanguage);
280 
281     info.dwScale++;
282     IS_INFO_UPDATED(dwScale);
283     info.dwScale = 0;
284     IS_INFO_UPDATED(dwScale);
285 
286     info.dwRate++;
287     IS_INFO_UPDATED(dwRate);
288     info.dwRate = 0;
289     IS_INFO_UPDATED(dwRate);
290 
291     info.dwQuality++;
292     IS_INFO_UPDATED(dwQuality);
293     info.dwQuality = 0;
294     IS_INFO_UPDATED(dwQuality);
295     info.dwQuality = -2;
296     IS_INFO_UPDATED(dwQuality);
297     info.dwQuality = ICQUALITY_HIGH+1;
298     IS_INFO_UPDATED(dwQuality);
299 
300     info.rcFrame.left = 0;
301     IS_INFO_UPDATED(rcFrame.left);
302     info.rcFrame.top = 0;
303     IS_INFO_UPDATED(rcFrame.top);
304     info.rcFrame.right = 0;
305     IS_INFO_UPDATED(rcFrame.right);
306     info.rcFrame.bottom = 0;
307     IS_INFO_UPDATED(rcFrame.bottom);
308 
309     info.rcFrame.left = -1;
310     IS_INFO_UPDATED(rcFrame.left);
311     info.rcFrame.top = -1;
312     IS_INFO_UPDATED(rcFrame.top);
313     info.rcFrame.right = -1;
314     IS_INFO_UPDATED(rcFrame.right);
315     info.rcFrame.bottom = -1;
316     IS_INFO_UPDATED(rcFrame.bottom);
317     AVIStreamRelease(stream);
318 #undef IS_INFO_UPDATED
319 }
320 
321 
322 static void init_test_struct(COMMON_AVI_HEADERS *cah)
323 {
324     memcpy(cah->fh, deffh, sizeof(deffh));
325     cah->mah = defmah;
326     cah->ash0 = defash0;
327     cah->ash1 = defash1;
328     cah->pcmwf = defpcmwf;
329 }
330 
331 static void create_avi_file(const COMMON_AVI_HEADERS *cah, char *filename)
332 {
333     HANDLE hFile;
334     DWORD written;
335 
336     hFile = CreateFileA(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
337 
338     ok(hFile != INVALID_HANDLE_VALUE, "Couldn't create file\n");
339 
340     WriteFile(hFile, &cah->fh, sizeof(deffh), &written, NULL);
341     WriteFile(hFile, &cah->mah, sizeof(MainAVIHeader), &written, NULL);
342     WriteFile(hFile, streamlist, sizeof(streamlist), &written, NULL);
343     WriteFile(hFile, &cah->ash0, 0x38, &written, NULL);
344     WriteFile(hFile, videostreamformat, sizeof(videostreamformat), &written, NULL);
345     WriteFile(hFile, padding1, sizeof(padding1), &written, NULL);
346     WriteFile(hFile, videopropheader, sizeof(videopropheader), &written, NULL);
347     WriteFile(hFile, &cah->ash1, 0x38, &written, NULL);
348     WriteFile(hFile, audiostreamformat_pre, sizeof(audiostreamformat_pre), &written, NULL);
349     WriteFile(hFile, &cah->pcmwf, sizeof(PCMWAVEFORMAT), &written, NULL);
350     WriteFile(hFile, data, sizeof(data), &written, NULL);
351 
352     CloseHandle(hFile);
353 }
354 
355 static void test_default_data(void)
356 {
357     COMMON_AVI_HEADERS cah;
358     char filename[MAX_PATH];
359     PAVIFILE pFile;
360     int res;
361     LONG lSize;
362     PAVISTREAM pStream0;
363     PAVISTREAM pStream1;
364     AVISTREAMINFOA asi0, asi1;
365     WAVEFORMATEX wfx;
366 
367     GetTempPathA(MAX_PATH, filename);
368     strcpy(filename+strlen(filename), testfilename);
369 
370     init_test_struct(&cah);
371     create_avi_file(&cah, filename);
372 
373     res = AVIFileOpenA(&pFile, filename, OF_SHARE_DENY_WRITE, 0L);
374     ok(res == 0, "Unable to open file: error=%u\n", res);
375 
376     pStream0 = (void *)0xdeadbeef;
377     res = AVIFileGetStream(pFile, &pStream0, ~0U, 0);
378     ok(res == AVIERR_NODATA, "expected AVIERR_NODATA, got %u\n", res);
379     ok(pStream0 == NULL, "AVIFileGetStream should set stream to NULL\n");
380 
381     res = AVIFileGetStream(pFile, &pStream0, 0, 0);
382     ok(res == 0, "Unable to open video stream: error=%u\n", res);
383 
384     res = AVIFileGetStream(pFile, &pStream1, 0, 1);
385     ok(res == 0, "Unable to open audio stream: error=%u\n", res);
386 
387     res = AVIStreamInfoA(pStream0, &asi0, sizeof(asi0));
388     ok(res == 0, "Unable to read stream info: error=%u\n", res);
389 
390     res = AVIStreamInfoA(pStream1, &asi1, sizeof(asi1));
391     ok(res == 0, "Unable to read stream info: error=%u\n", res);
392 
393     res = AVIStreamReadFormat(pStream0, AVIStreamStart(pStream1), NULL, &lSize);
394     ok(res == 0, "Unable to read format size: error=%u\n", res);
395 
396     res = AVIStreamReadFormat(pStream1, AVIStreamStart(pStream1), &wfx, &lSize);
397     ok(res == 0, "Unable to read format: error=%u\n", res);
398 
399     ok(asi0.fccType == streamtypeVIDEO, "got 0x%x (expected streamtypeVIDEO)\n", asi0.fccType);
400     ok(asi0.fccHandler == 0x30323449, "got 0x%x (expected 0x30323449)\n", asi0.fccHandler);
401     ok(asi0.dwFlags == 0, "got %u (expected 0)\n", asi0.dwFlags);
402     ok(asi0.wPriority == 0, "got %u (expected 0)\n", asi0.wPriority);
403     ok(asi0.wLanguage == 0, "got %u (expected 0)\n", asi0.wLanguage);
404     ok(asi0.dwScale == 1001, "got %u (expected 1001)\n", asi0.dwScale);
405     ok(asi0.dwRate == 30000, "got %u (expected 30000)\n", asi0.dwRate);
406     ok(asi0.dwStart == 0, "got %u (expected 0)\n", asi0.dwStart);
407     ok(asi0.dwLength == 1, "got %u (expected 1)\n", asi0.dwLength);
408     ok(asi0.dwInitialFrames == 0, "got %u (expected 0)\n", asi0.dwInitialFrames);
409     ok(asi0.dwSuggestedBufferSize == 0, "got %u (expected 0)\n", asi0.dwSuggestedBufferSize);
410     ok(asi0.dwQuality == 0xffffffff, "got 0x%x (expected 0xffffffff)\n", asi0.dwQuality);
411     ok(asi0.dwSampleSize == 0, "got %u (expected 0)\n", asi0.dwSampleSize);
412     ok(asi0.rcFrame.left == 0, "got %u (expected 0)\n", asi0.rcFrame.left);
413     ok(asi0.rcFrame.top == 0, "got %u (expected 0)\n", asi0.rcFrame.top);
414     ok(asi0.rcFrame.right == 8, "got %u (expected 8)\n", asi0.rcFrame.right);  /* these are based on the values in the mah and not */
415     ok(asi0.rcFrame.bottom == 6, "got %u (expected 6)\n", asi0.rcFrame.bottom);/* on the ones in the ash which are 0 here */
416     ok(asi0.dwEditCount == 0, "got %u (expected 0)\n", asi0.dwEditCount);
417     ok(asi0.dwFormatChangeCount == 0, "got %u (expected 0)\n", asi0.dwFormatChangeCount);
418 
419     ok(asi1.fccType == streamtypeAUDIO, "got 0x%x (expected streamtypeVIDEO)\n", asi1.fccType);
420     ok(asi1.fccHandler == 0x1, "got 0x%x (expected 0x1)\n", asi1.fccHandler);
421     ok(asi1.dwFlags == 0, "got %u (expected 0)\n", asi1.dwFlags);
422     ok(asi1.wPriority == 0, "got %u (expected 0)\n", asi1.wPriority);
423     ok(asi1.wLanguage == 0, "got %u (expected 0)\n", asi1.wLanguage);
424     ok(asi1.dwScale == 1, "got %u (expected 1)\n", asi1.dwScale);
425     ok(asi1.dwRate == 11025, "got %u (expected 11025)\n", asi1.dwRate);
426     ok(asi1.dwStart == 0, "got %u (expected 0)\n", asi1.dwStart);
427     ok(asi1.dwLength == 1637, "got %u (expected 1637)\n", asi1.dwLength);
428     ok(asi1.dwInitialFrames == 0, "got %u (expected 0)\n", asi1.dwInitialFrames);
429     ok(asi1.dwSuggestedBufferSize == 0, "got %u (expected 0)\n", asi1.dwSuggestedBufferSize);
430     ok(asi1.dwQuality == 0xffffffff, "got 0x%x (expected 0xffffffff)\n", asi1.dwQuality);
431     ok(asi1.dwSampleSize == 2, "got %u (expected 2)\n", asi1.dwSampleSize);
432     ok(asi1.rcFrame.left == 0, "got %u (expected 0)\n", asi1.rcFrame.left);
433     ok(asi1.rcFrame.top == 0, "got %u (expected 0)\n", asi1.rcFrame.top);
434     ok(asi1.rcFrame.right == 0, "got %u (expected 0)\n", asi1.rcFrame.right);
435     ok(asi1.rcFrame.bottom == 0, "got %u (expected 0)\n", asi1.rcFrame.bottom);
436     ok(asi1.dwEditCount == 0, "got %u (expected 0)\n", asi1.dwEditCount);
437     ok(asi1.dwFormatChangeCount == 0, "got %u (expected 0)\n", asi1.dwFormatChangeCount);
438 
439     ok(wfx.wFormatTag == 1, "got %u (expected 1)\n",wfx.wFormatTag);
440     ok(wfx.nChannels == 2, "got %u (expected 2)\n",wfx.nChannels);
441     ok(wfx.wFormatTag == 1, "got %u (expected 1)\n",wfx.wFormatTag);
442     ok(wfx.nSamplesPerSec == 11025, "got %u (expected 11025)\n",wfx.nSamplesPerSec);
443     ok(wfx.nAvgBytesPerSec == 22050, "got %u (expected 22050)\n",wfx.nAvgBytesPerSec);
444     ok(wfx.nBlockAlign == 2, "got %u (expected 2)\n",wfx.nBlockAlign);
445 
446     AVIStreamRelease(pStream0);
447     AVIStreamRelease(pStream1);
448     AVIFileRelease(pFile);
449     ok(DeleteFileA(filename) !=0, "Deleting file %s failed\n", filename);
450 }
451 
452 static void test_amh_corruption(void)
453 {
454     COMMON_AVI_HEADERS cah;
455     char filename[MAX_PATH];
456     PAVIFILE pFile;
457     int res;
458 
459     GetTempPathA(MAX_PATH, filename);
460     strcpy(filename+strlen(filename), testfilename);
461 
462     /* Make sure only AVI files with the proper headers will be loaded */
463     init_test_struct(&cah);
464     cah.fh[3] = mmioFOURCC('A', 'V', 'i', ' ');
465 
466     create_avi_file(&cah, filename);
467     res = AVIFileOpenA(&pFile, filename, OF_SHARE_DENY_WRITE, 0L);
468     ok(res != 0, "Able to open file: error=%u\n", res);
469 
470     ok(DeleteFileA(filename) !=0, "Deleting file %s failed\n", filename);
471 }
472 
473 static void test_ash1_corruption(void)
474 {
475     COMMON_AVI_HEADERS cah;
476     char filename[MAX_PATH];
477     PAVIFILE pFile;
478     int res;
479     PAVISTREAM pStream1;
480     AVISTREAMINFOA asi1;
481 
482     GetTempPathA(MAX_PATH, filename);
483     strcpy(filename+strlen(filename), testfilename);
484 
485     /* Corrupt the sample size in the audio stream header */
486     init_test_struct(&cah);
487     cah.ash1.dwSampleSize = 0xdeadbeef;
488 
489     create_avi_file(&cah, filename);
490 
491     res = AVIFileOpenA(&pFile, filename, OF_SHARE_DENY_WRITE, 0L);
492     ok(res == 0, "Unable to open file: error=%u\n", res);
493 
494     res = AVIFileGetStream(pFile, &pStream1, 0, 1);
495     ok(res == 0, "Unable to open audio stream: error=%u\n", res);
496 
497     res = AVIStreamInfoA(pStream1, &asi1, sizeof(asi1));
498     ok(res == 0, "Unable to read stream info: error=%u\n", res);
499 
500     /* The result will still be 2, because the value is dynamically replaced with the nBlockAlign
501        value from the stream format header. The next test will prove this */
502     ok(asi1.dwSampleSize == 2, "got %u (expected 2)\n", asi1.dwSampleSize);
503 
504     AVIStreamRelease(pStream1);
505     AVIFileRelease(pFile);
506     ok(DeleteFileA(filename) !=0, "Deleting file %s failed\n", filename);
507 }
508 
509 static void test_ash1_corruption2(void)
510 {
511     COMMON_AVI_HEADERS cah;
512     char filename[MAX_PATH];
513     PAVIFILE pFile;
514     int res;
515     PAVISTREAM pStream1;
516     AVISTREAMINFOA asi1;
517 
518     GetTempPathA(MAX_PATH, filename);
519     strcpy(filename+strlen(filename), testfilename);
520 
521     /* Corrupt the block alignment in the audio format header */
522     init_test_struct(&cah);
523     cah.pcmwf.wf.nBlockAlign = 0xdead;
524 
525     create_avi_file(&cah, filename);
526 
527     res = AVIFileOpenA(&pFile, filename, OF_SHARE_DENY_WRITE, 0L);
528     ok(res == 0, "Unable to open file: error=%u\n", res);
529 
530     res = AVIFileGetStream(pFile, &pStream1, 0, 1);
531     ok(res == 0, "Unable to open audio stream: error=%u\n", res);
532 
533     ok(AVIStreamInfoA(pStream1, &asi1, sizeof(asi1)) == 0, "Unable to read stream info\n");
534 
535     /* The result will also be the corrupt value, as explained above. */
536     ok(asi1.dwSampleSize == 0xdead, "got 0x%x (expected 0xdead)\n", asi1.dwSampleSize);
537 
538     AVIStreamRelease(pStream1);
539     AVIFileRelease(pFile);
540     ok(DeleteFileA(filename) !=0, "Deleting file %s failed\n", filename);
541 }
542 
543 /* Outer IUnknown for COM aggregation tests */
544 struct unk_impl {
545     IUnknown IUnknown_iface;
546     LONG ref;
547     IUnknown *inner_unk;
548 };
549 
550 static inline struct unk_impl *impl_from_IUnknown(IUnknown *iface)
551 {
552     return CONTAINING_RECORD(iface, struct unk_impl, IUnknown_iface);
553 }
554 
555 static HRESULT WINAPI unk_QueryInterface(IUnknown *iface, REFIID riid, void **ppv)
556 {
557     struct unk_impl *This = impl_from_IUnknown(iface);
558     LONG ref = This->ref;
559     HRESULT hr;
560 
561     if (IsEqualGUID(riid, &IID_IUnknown))
562     {
563         *ppv = iface;
564         IUnknown_AddRef(iface);
565         return S_OK;
566     }
567 
568     hr = IUnknown_QueryInterface(This->inner_unk, riid, ppv);
569     if (hr == S_OK)
570     {
571         trace("Working around COM aggregation ref counting bug\n");
572         ok(ref == This->ref, "Outer ref count expected %d got %d\n", ref, This->ref);
573         IUnknown_AddRef((IUnknown*)*ppv);
574         ref = IUnknown_Release(This->inner_unk);
575         ok(ref == 1, "Inner ref count expected 1 got %d\n", ref);
576     }
577 
578     return hr;
579 }
580 
581 static ULONG WINAPI unk_AddRef(IUnknown *iface)
582 {
583     struct unk_impl *This = impl_from_IUnknown(iface);
584 
585     return InterlockedIncrement(&This->ref);
586 }
587 
588 static ULONG WINAPI unk_Release(IUnknown *iface)
589 {
590     struct unk_impl *This = impl_from_IUnknown(iface);
591 
592     return InterlockedDecrement(&This->ref);
593 }
594 
595 static const IUnknownVtbl unk_vtbl =
596 {
597     unk_QueryInterface,
598     unk_AddRef,
599     unk_Release
600 };
601 
602 static void test_COM(void)
603 {
604     struct unk_impl unk_obj = {{&unk_vtbl}, 19, NULL};
605     IAVIFile *avif = NULL;
606     IPersistFile *pf;
607     IUnknown *unk;
608     LONG refcount;
609     HRESULT hr;
610 
611     /* COM aggregation */
612     hr = CoCreateInstance(&CLSID_AVIFile, &unk_obj.IUnknown_iface, CLSCTX_INPROC_SERVER,
613             &IID_IUnknown, (void**)&unk_obj.inner_unk);
614     ok(hr == S_OK, "COM aggregation failed: %08x, expected S_OK\n", hr);
615     hr = IUnknown_QueryInterface(&unk_obj.IUnknown_iface, &IID_IAVIFile, (void**)&avif);
616     ok(hr == S_OK, "QueryInterface for IID_IAVIFile failed: %08x\n", hr);
617     refcount = IAVIFile_AddRef(avif);
618     ok(refcount == unk_obj.ref, "AVIFile just pretends to support COM aggregation\n");
619     refcount = IAVIFile_Release(avif);
620     ok(refcount == unk_obj.ref, "AVIFile just pretends to support COM aggregation\n");
621     hr = IAVIFile_QueryInterface(avif, &IID_IPersistFile, (void**)&pf);
622     ok(hr == S_OK, "QueryInterface for IID_IPersistFile failed: %08x\n", hr);
623     refcount = IPersistFile_Release(pf);
624     ok(refcount == unk_obj.ref, "AVIFile just pretends to support COM aggregation\n");
625     refcount = IAVIFile_Release(avif);
626     ok(refcount == 19, "Outer ref count should be back at 19 but is %d\n", refcount);
627     refcount = IUnknown_Release(unk_obj.inner_unk);
628     ok(refcount == 0, "Inner ref count should be 0 but is %u\n", refcount);
629 
630     /* Invalid RIID */
631     hr = CoCreateInstance(&CLSID_AVIFile, NULL, CLSCTX_INPROC_SERVER, &IID_IAVIStream,
632             (void**)&avif);
633     ok(hr == E_NOINTERFACE, "AVIFile create failed: %08x, expected E_NOINTERFACE\n", hr);
634 
635     /* Same refcount */
636     hr = CoCreateInstance(&CLSID_AVIFile, NULL, CLSCTX_INPROC_SERVER, &IID_IAVIFile, (void**)&avif);
637     ok(hr == S_OK, "AVIFile create failed: %08x, expected S_OK\n", hr);
638     refcount = IAVIFile_AddRef(avif);
639     ok(refcount == 2, "refcount == %u, expected 2\n", refcount);
640     hr = IAVIFile_QueryInterface(avif, &IID_IUnknown, (void**)&unk);
641     ok(hr == S_OK, "QueryInterface for IID_IUnknown failed: %08x\n", hr);
642     refcount = IUnknown_AddRef(unk);
643     ok(refcount == 4, "refcount == %u, expected 4\n", refcount);
644     hr = IAVIFile_QueryInterface(avif, &IID_IPersistFile, (void**)&pf);
645     ok(hr == S_OK, "QueryInterface for IID_IPersistFile failed: %08x\n", hr);
646     refcount = IPersistFile_AddRef(pf);
647     ok(refcount == 6, "refcount == %u, expected 6\n", refcount);
648 
649     while (IAVIFile_Release(avif));
650 }
651 
652 static void test_COM_wavfile(void)
653 {
654     struct unk_impl unk_obj = {{&unk_vtbl}, 19, NULL};
655     IAVIFile *avif = NULL;
656     IPersistFile *pf;
657     IAVIStream *avis;
658     IUnknown *unk;
659     ULONG refcount;
660     HRESULT hr;
661 
662     /* COM aggregation */
663     hr = CoCreateInstance(&CLSID_WAVFile, &unk_obj.IUnknown_iface, CLSCTX_INPROC_SERVER,
664             &IID_IUnknown, (void**)&unk_obj.inner_unk);
665     ok(hr == S_OK, "COM aggregation failed: %08x, expected S_OK\n", hr);
666     hr = IUnknown_QueryInterface(&unk_obj.IUnknown_iface, &IID_IAVIFile, (void**)&avif);
667     ok(hr == S_OK, "QueryInterface for IID_IAVIFile failed: %08x\n", hr);
668     refcount = IAVIFile_AddRef(avif);
669     ok(refcount == unk_obj.ref, "WAVFile just pretends to support COM aggregation\n");
670     refcount = IAVIFile_Release(avif);
671     ok(refcount == unk_obj.ref, "WAVFile just pretends to support COM aggregation\n");
672     hr = IAVIFile_QueryInterface(avif, &IID_IPersistFile, (void**)&pf);
673     ok(hr == S_OK, "QueryInterface for IID_IPersistFile failed: %08x\n", hr);
674     refcount = IPersistFile_Release(pf);
675     ok(refcount == unk_obj.ref, "WAVFile just pretends to support COM aggregation\n");
676     refcount = IAVIFile_Release(avif);
677     ok(refcount == 19, "Outer ref count should be back at 19 but is %d\n", refcount);
678     refcount = IUnknown_Release(unk_obj.inner_unk);
679     ok(refcount == 0, "Inner ref count should be 0 but is %u\n", refcount);
680 
681     /* Invalid RIID */
682     hr = CoCreateInstance(&CLSID_WAVFile, NULL, CLSCTX_INPROC_SERVER, &IID_IAVIStreaming,
683             (void**)&avif);
684     ok(hr == E_NOINTERFACE, "WAVFile create failed: %08x, expected E_NOINTERFACE\n", hr);
685 
686     /* Same refcount for all WAVFile interfaces */
687     hr = CoCreateInstance(&CLSID_WAVFile, NULL, CLSCTX_INPROC_SERVER, &IID_IAVIFile, (void**)&avif);
688     ok(hr == S_OK, "WAVFile create failed: %08x, expected S_OK\n", hr);
689     refcount = IAVIFile_AddRef(avif);
690     ok(refcount == 2, "refcount == %u, expected 2\n", refcount);
691 
692     hr = IAVIFile_QueryInterface(avif, &IID_IPersistFile, (void**)&pf);
693     ok(hr == S_OK, "QueryInterface for IID_IPersistFile failed: %08x\n", hr);
694     refcount = IPersistFile_AddRef(pf);
695     ok(refcount == 4, "refcount == %u, expected 4\n", refcount);
696     refcount = IPersistFile_Release(pf);
697 
698     hr = IAVIFile_QueryInterface(avif, &IID_IAVIStream, (void**)&avis);
699     ok(hr == S_OK, "QueryInterface for IID_IAVIStream failed: %08x\n", hr);
700     refcount = IAVIStream_AddRef(avis);
701     ok(refcount == 5, "refcount == %u, expected 5\n", refcount);
702     refcount = IAVIStream_Release(avis);
703 
704     hr = IAVIFile_QueryInterface(avif, &IID_IUnknown, (void**)&unk);
705     ok(hr == S_OK, "QueryInterface for IID_IUnknown failed: %08x\n", hr);
706     refcount = IUnknown_AddRef(unk);
707     ok(refcount == 6, "refcount == %u, expected 6\n", refcount);
708     refcount = IUnknown_Release(unk);
709 
710     while (IAVIFile_Release(avif));
711 }
712 
713 static void test_COM_editstream(void)
714 {
715     IAVIEditStream *edit;
716     IAVIStream *stream;
717     IUnknown *unk;
718     ULONG refcount;
719     HRESULT hr;
720 
721     /* Same refcount for all AVIEditStream interfaces */
722     hr = CreateEditableStream(&stream, NULL);
723     ok(hr == S_OK, "AVIEditStream create failed: %08x, expected S_OK\n", hr);
724     refcount = IAVIStream_AddRef(stream);
725     ok(refcount == 2, "refcount == %u, expected 2\n", refcount);
726 
727     hr = IAVIStream_QueryInterface(stream, &IID_IAVIEditStream, (void**)&edit);
728     ok(hr == S_OK, "QueryInterface for IID_IAVIEditStream failed: %08x\n", hr);
729     refcount = IAVIEditStream_AddRef(edit);
730     ok(refcount == 4, "refcount == %u, expected 4\n", refcount);
731     refcount = IAVIEditStream_Release(edit);
732 
733     hr = IAVIEditStream_QueryInterface(edit, &IID_IUnknown, (void**)&unk);
734     ok(hr == S_OK, "QueryInterface for IID_IUnknown failed: %08x\n", hr);
735     refcount = IUnknown_AddRef(unk);
736     ok(refcount == 5, "refcount == %u, expected 5\n", refcount);
737     IUnknown_Release(unk);
738 
739     while (IAVIEditStream_Release(edit));
740 }
741 
742 START_TEST(api)
743 {
744 
745     AVIFileInit();
746     test_EditStreamSetInfo();
747     test_AVISaveOptions();
748     test_default_data();
749     test_amh_corruption();
750     test_ash1_corruption();
751     test_ash1_corruption2();
752     test_COM();
753     test_COM_wavfile();
754     test_COM_editstream();
755     AVIFileExit();
756 
757 }
758