xref: /reactos/dll/win32/avifil32/api.c (revision 12e94103)
1 /*
2  * Copyright 1999 Marcus Meissner
3  * Copyright 2002-2003 Michael Günnewig
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
18  */
19 
20 #include <stdarg.h>
21 
22 #define COBJMACROS
23 
24 #include "windef.h"
25 #include "winbase.h"
26 #include "winnls.h"
27 #include "wingdi.h"
28 #include "winuser.h"
29 #include "winreg.h"
30 #include "winerror.h"
31 
32 #include "ole2.h"
33 #include "shellapi.h"
34 #include "shlobj.h"
35 #include "vfw.h"
36 #include "msacm.h"
37 
38 #include "avifile_private.h"
39 
40 #include "wine/debug.h"
41 #include "wine/unicode.h"
42 
43 WINE_DEFAULT_DEBUG_CHANNEL(avifile);
44 
45 
46 /***********************************************************************
47  * for AVIBuildFilterW -- uses fixed size table
48  */
49 #define MAX_FILTERS 30 /* 30 => 7kB */
50 
51 typedef struct _AVIFilter {
52   WCHAR szClsid[40];
53   WCHAR szExtensions[MAX_FILTERS * 7];
54 } AVIFilter;
55 
56 /***********************************************************************
57  * for AVISaveOptions
58  */
59 static struct {
60   UINT                  uFlags;
61   INT                   nStreams;
62   PAVISTREAM           *ppavis;
63   LPAVICOMPRESSOPTIONS *ppOptions;
64   INT                   nCurrent;
65 } SaveOpts;
66 
67 /***********************************************************************
68  * copied from dlls/ole32/compobj.c
69  */
70 static HRESULT AVIFILE_CLSIDFromString(LPCSTR idstr, LPCLSID id)
71 {
72   BYTE const *s;
73   BYTE *p;
74   INT   i;
75   BYTE table[256];
76 
77   if (!idstr) {
78     memset(id, 0, sizeof(CLSID));
79     return S_OK;
80   }
81 
82   /* validate the CLSID string */
83   if (lstrlenA(idstr) != 38)
84     return CO_E_CLASSSTRING;
85 
86   s = (BYTE const*)idstr;
87   if ((s[0]!='{') || (s[9]!='-') || (s[14]!='-') || (s[19]!='-') ||
88       (s[24]!='-') || (s[37]!='}'))
89     return CO_E_CLASSSTRING;
90 
91   for (i = 1; i < 37; i++) {
92     if ((i == 9) || (i == 14) || (i == 19) || (i == 24))
93       continue;
94     if (!(((s[i] >= '0') && (s[i] <= '9'))  ||
95         ((s[i] >= 'a') && (s[i] <= 'f'))  ||
96         ((s[i] >= 'A') && (s[i] <= 'F')))
97        )
98       return CO_E_CLASSSTRING;
99   }
100 
101   TRACE("%s -> %p\n", s, id);
102 
103   /* quick lookup table */
104   memset(table, 0, 256);
105 
106   for (i = 0; i < 10; i++)
107     table['0' + i] = i;
108 
109   for (i = 0; i < 6; i++) {
110     table['A' + i] = i+10;
111     table['a' + i] = i+10;
112   }
113 
114   /* in form {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX} */
115   p = (BYTE *) id;
116 
117   s++;	/* skip leading brace  */
118   for (i = 0; i < 4; i++) {
119     p[3 - i] = table[*s]<<4 | table[*(s+1)];
120     s += 2;
121   }
122   p += 4;
123   s++;	/* skip - */
124 
125   for (i = 0; i < 2; i++) {
126     p[1-i] = table[*s]<<4 | table[*(s+1)];
127     s += 2;
128   }
129   p += 2;
130   s++;	/* skip - */
131 
132   for (i = 0; i < 2; i++) {
133     p[1-i] = table[*s]<<4 | table[*(s+1)];
134     s += 2;
135   }
136   p += 2;
137   s++;	/* skip - */
138 
139   /* these are just sequential bytes */
140   for (i = 0; i < 2; i++) {
141     *p++ = table[*s]<<4 | table[*(s+1)];
142     s += 2;
143   }
144   s++;	/* skip - */
145 
146   for (i = 0; i < 6; i++) {
147     *p++ = table[*s]<<4 | table[*(s+1)];
148     s += 2;
149   }
150 
151   return S_OK;
152 }
153 
154 static BOOL AVIFILE_GetFileHandlerByExtension(LPCWSTR szFile, LPCLSID lpclsid)
155 {
156   CHAR   szRegKey[25];
157   CHAR   szValue[100];
158   LPWSTR szExt = strrchrW(szFile, '.');
159   LONG   len = sizeof(szValue) / sizeof(szValue[0]);
160 
161   if (szExt == NULL)
162     return FALSE;
163 
164   szExt++;
165 
166   wsprintfA(szRegKey, "AVIFile\\Extensions\\%.3ls", szExt);
167   if (RegQueryValueA(HKEY_CLASSES_ROOT, szRegKey, szValue, &len) != ERROR_SUCCESS)
168     return FALSE;
169 
170   return (AVIFILE_CLSIDFromString(szValue, lpclsid) == S_OK);
171 }
172 
173 /***********************************************************************
174  *		AVIFileInit		(AVIFIL32.@)
175  */
176 void WINAPI AVIFileInit(void) {
177   OleInitialize(NULL);
178 }
179 
180 /***********************************************************************
181  *		AVIFileExit		(AVIFIL32.@)
182  */
183 void WINAPI AVIFileExit(void) {
184   /* need to free ole32.dll if we are the last exit call */
185   /* OleUninitialize() */
186   FIXME("(): stub!\n");
187 }
188 
189 /***********************************************************************
190  *		AVIFileOpen		(AVIFIL32.@)
191  *		AVIFileOpenA		(AVIFIL32.@)
192  */
193 HRESULT WINAPI AVIFileOpenA(PAVIFILE *ppfile, LPCSTR szFile, UINT uMode,
194 			    LPCLSID lpHandler)
195 {
196   LPWSTR  wszFile = NULL;
197   HRESULT hr;
198   int     len;
199 
200   TRACE("(%p,%s,0x%08X,%s)\n", ppfile, debugstr_a(szFile), uMode,
201 	debugstr_guid(lpHandler));
202 
203   /* check parameters */
204   if (ppfile == NULL || szFile == NULL)
205     return AVIERR_BADPARAM;
206 
207   /* convert ASCII string to Unicode and call unicode function */
208   len = MultiByteToWideChar(CP_ACP, 0, szFile, -1, NULL, 0);
209   if (len <= 0)
210     return AVIERR_BADPARAM;
211 
212   wszFile = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
213   if (wszFile == NULL)
214     return AVIERR_MEMORY;
215 
216   MultiByteToWideChar(CP_ACP, 0, szFile, -1, wszFile, len);
217 
218   hr = AVIFileOpenW(ppfile, wszFile, uMode, lpHandler);
219 
220   HeapFree(GetProcessHeap(), 0, wszFile);
221 
222   return hr;
223 }
224 
225 /***********************************************************************
226  *		AVIFileOpenW		(AVIFIL32.@)
227  */
228 HRESULT WINAPI AVIFileOpenW(PAVIFILE *ppfile, LPCWSTR szFile, UINT uMode,
229 			    LPCLSID lpHandler)
230 {
231   IPersistFile *ppersist = NULL;
232   CLSID         clsidHandler;
233   HRESULT       hr;
234 
235   TRACE("(%p,%s,0x%X,%s)\n", ppfile, debugstr_w(szFile), uMode,
236 	debugstr_guid(lpHandler));
237 
238   /* check parameters */
239   if (ppfile == NULL || szFile == NULL)
240     return AVIERR_BADPARAM;
241 
242   *ppfile = NULL;
243 
244   /* if no handler then try guessing it by extension */
245   if (lpHandler == NULL) {
246     if (! AVIFILE_GetFileHandlerByExtension(szFile, &clsidHandler))
247       clsidHandler = CLSID_AVIFile;
248   } else
249     clsidHandler = *lpHandler;
250 
251   /* create instance of handler */
252   hr = CoCreateInstance(&clsidHandler, NULL, CLSCTX_INPROC, &IID_IAVIFile, (LPVOID*)ppfile);
253   if (FAILED(hr) || *ppfile == NULL)
254     return hr;
255 
256   /* ask for IPersistFile interface for loading/creating the file */
257   hr = IAVIFile_QueryInterface(*ppfile, &IID_IPersistFile, (LPVOID*)&ppersist);
258   if (FAILED(hr) || ppersist == NULL) {
259     IAVIFile_Release(*ppfile);
260     *ppfile = NULL;
261     return hr;
262   }
263 
264   hr = IPersistFile_Load(ppersist, szFile, uMode);
265   IPersistFile_Release(ppersist);
266   if (FAILED(hr)) {
267     IAVIFile_Release(*ppfile);
268     *ppfile = NULL;
269   }
270 
271   return hr;
272 }
273 
274 /***********************************************************************
275  *		AVIFileAddRef		(AVIFIL32.@)
276  */
277 ULONG WINAPI AVIFileAddRef(PAVIFILE pfile)
278 {
279   TRACE("(%p)\n", pfile);
280 
281   if (pfile == NULL) {
282     ERR(": bad handle passed!\n");
283     return 0;
284   }
285 
286   return IAVIFile_AddRef(pfile);
287 }
288 
289 /***********************************************************************
290  *		AVIFileRelease		(AVIFIL32.@)
291  */
292 ULONG WINAPI AVIFileRelease(PAVIFILE pfile)
293 {
294   TRACE("(%p)\n", pfile);
295 
296   if (pfile == NULL) {
297     ERR(": bad handle passed!\n");
298     return 0;
299   }
300 
301   return IAVIFile_Release(pfile);
302 }
303 
304 /***********************************************************************
305  *		AVIFileInfo		(AVIFIL32.@)
306  *		AVIFileInfoA		(AVIFIL32.@)
307  */
308 HRESULT WINAPI AVIFileInfoA(PAVIFILE pfile, LPAVIFILEINFOA afi, LONG size)
309 {
310   AVIFILEINFOW afiw;
311   HRESULT      hres;
312 
313   TRACE("(%p,%p,%d)\n", pfile, afi, size);
314 
315   if (pfile == NULL)
316     return AVIERR_BADHANDLE;
317   if ((DWORD)size < sizeof(AVIFILEINFOA))
318     return AVIERR_BADSIZE;
319 
320   hres = IAVIFile_Info(pfile, &afiw, sizeof(afiw));
321 
322   memcpy(afi, &afiw, sizeof(*afi) - sizeof(afi->szFileType));
323   WideCharToMultiByte(CP_ACP, 0, afiw.szFileType, -1, afi->szFileType,
324 		      sizeof(afi->szFileType), NULL, NULL);
325   afi->szFileType[sizeof(afi->szFileType) - 1] = 0;
326 
327   return hres;
328 }
329 
330 /***********************************************************************
331  *		AVIFileInfoW		(AVIFIL32.@)
332  */
333 HRESULT WINAPI AVIFileInfoW(PAVIFILE pfile, LPAVIFILEINFOW afiw, LONG size)
334 {
335   TRACE("(%p,%p,%d)\n", pfile, afiw, size);
336 
337   if (pfile == NULL)
338     return AVIERR_BADHANDLE;
339 
340   return IAVIFile_Info(pfile, afiw, size);
341 }
342 
343 /***********************************************************************
344  *		AVIFileGetStream	(AVIFIL32.@)
345  */
346 HRESULT WINAPI AVIFileGetStream(PAVIFILE pfile, PAVISTREAM *avis,
347 				DWORD fccType, LONG lParam)
348 {
349   TRACE("(%p,%p,'%4.4s',%d)\n", pfile, avis, (char*)&fccType, lParam);
350 
351   if (pfile == NULL)
352     return AVIERR_BADHANDLE;
353 
354   return IAVIFile_GetStream(pfile, avis, fccType, lParam);
355 }
356 
357 /***********************************************************************
358  *		AVIFileCreateStream	(AVIFIL32.@)
359  *		AVIFileCreateStreamA	(AVIFIL32.@)
360  */
361 HRESULT WINAPI AVIFileCreateStreamA(PAVIFILE pfile, PAVISTREAM *ppavi,
362 				    LPAVISTREAMINFOA psi)
363 {
364   AVISTREAMINFOW	psiw;
365 
366   TRACE("(%p,%p,%p)\n", pfile, ppavi, psi);
367 
368   if (pfile == NULL)
369     return AVIERR_BADHANDLE;
370 
371   /* Only the szName at the end is different */
372   memcpy(&psiw, psi, sizeof(*psi) - sizeof(psi->szName));
373   MultiByteToWideChar(CP_ACP, 0, psi->szName, -1, psiw.szName,
374 		      sizeof(psiw.szName) / sizeof(psiw.szName[0]));
375 
376   return IAVIFile_CreateStream(pfile, ppavi, &psiw);
377 }
378 
379 /***********************************************************************
380  *		AVIFileCreateStreamW	(AVIFIL32.@)
381  */
382 HRESULT WINAPI AVIFileCreateStreamW(PAVIFILE pfile, PAVISTREAM *avis,
383 				    LPAVISTREAMINFOW asi)
384 {
385   TRACE("(%p,%p,%p)\n", pfile, avis, asi);
386 
387   if (pfile == NULL)
388     return AVIERR_BADHANDLE;
389 
390   return IAVIFile_CreateStream(pfile, avis, asi);
391 }
392 
393 /***********************************************************************
394  *		AVIFileWriteData	(AVIFIL32.@)
395  */
396 HRESULT WINAPI AVIFileWriteData(PAVIFILE pfile,DWORD fcc,LPVOID lp,LONG size)
397 {
398   TRACE("(%p,'%4.4s',%p,%d)\n", pfile, (char*)&fcc, lp, size);
399 
400   if (pfile == NULL)
401     return AVIERR_BADHANDLE;
402 
403   return IAVIFile_WriteData(pfile, fcc, lp, size);
404 }
405 
406 /***********************************************************************
407  *		AVIFileReadData		(AVIFIL32.@)
408  */
409 HRESULT WINAPI AVIFileReadData(PAVIFILE pfile,DWORD fcc,LPVOID lp,LPLONG size)
410 {
411   TRACE("(%p,'%4.4s',%p,%p)\n", pfile, (char*)&fcc, lp, size);
412 
413   if (pfile == NULL)
414     return AVIERR_BADHANDLE;
415 
416   return IAVIFile_ReadData(pfile, fcc, lp, size);
417 }
418 
419 /***********************************************************************
420  *		AVIFileEndRecord	(AVIFIL32.@)
421  */
422 HRESULT WINAPI AVIFileEndRecord(PAVIFILE pfile)
423 {
424   TRACE("(%p)\n", pfile);
425 
426   if (pfile == NULL)
427     return AVIERR_BADHANDLE;
428 
429   return IAVIFile_EndRecord(pfile);
430 }
431 
432 /***********************************************************************
433  *		AVIStreamAddRef		(AVIFIL32.@)
434  */
435 ULONG WINAPI AVIStreamAddRef(PAVISTREAM pstream)
436 {
437   TRACE("(%p)\n", pstream);
438 
439   if (pstream == NULL) {
440     ERR(": bad handle passed!\n");
441     return 0;
442   }
443 
444   return IAVIStream_AddRef(pstream);
445 }
446 
447 /***********************************************************************
448  *		AVIStreamRelease	(AVIFIL32.@)
449  */
450 ULONG WINAPI AVIStreamRelease(PAVISTREAM pstream)
451 {
452   TRACE("(%p)\n", pstream);
453 
454   if (pstream == NULL) {
455     ERR(": bad handle passed!\n");
456     return 0;
457   }
458 
459   return IAVIStream_Release(pstream);
460 }
461 
462 /***********************************************************************
463  *		AVIStreamCreate		(AVIFIL32.@)
464  */
465 HRESULT WINAPI AVIStreamCreate(PAVISTREAM *ppavi, LONG lParam1, LONG lParam2,
466 			       LPCLSID pclsidHandler)
467 {
468   HRESULT hr;
469 
470   TRACE("(%p,0x%08X,0x%08X,%s)\n", ppavi, lParam1, lParam2,
471 	debugstr_guid(pclsidHandler));
472 
473   if (ppavi == NULL)
474     return AVIERR_BADPARAM;
475 
476   *ppavi = NULL;
477   if (pclsidHandler == NULL)
478     return AVIERR_UNSUPPORTED;
479 
480   hr = CoCreateInstance(pclsidHandler, NULL, CLSCTX_INPROC, &IID_IAVIStream, (LPVOID*)ppavi);
481   if (FAILED(hr) || *ppavi == NULL)
482     return hr;
483 
484   hr = IAVIStream_Create(*ppavi, lParam1, lParam2);
485   if (FAILED(hr)) {
486     IAVIStream_Release(*ppavi);
487     *ppavi = NULL;
488   }
489 
490   return hr;
491 }
492 
493 /***********************************************************************
494  *		AVIStreamInfo		(AVIFIL32.@)
495  *		AVIStreamInfoA		(AVIFIL32.@)
496  */
497 HRESULT WINAPI AVIStreamInfoA(PAVISTREAM pstream, LPAVISTREAMINFOA asi,
498 			      LONG size)
499 {
500   AVISTREAMINFOW asiw;
501   HRESULT	 hres;
502 
503   TRACE("(%p,%p,%d)\n", pstream, asi, size);
504 
505   if (pstream == NULL)
506     return AVIERR_BADHANDLE;
507   if ((DWORD)size < sizeof(AVISTREAMINFOA))
508     return AVIERR_BADSIZE;
509 
510   hres = IAVIStream_Info(pstream, &asiw, sizeof(asiw));
511 
512   memcpy(asi, &asiw, sizeof(asiw) - sizeof(asiw.szName));
513   WideCharToMultiByte(CP_ACP, 0, asiw.szName, -1, asi->szName,
514 		      sizeof(asi->szName), NULL, NULL);
515   asi->szName[sizeof(asi->szName) - 1] = 0;
516 
517   return hres;
518 }
519 
520 /***********************************************************************
521  *		AVIStreamInfoW		(AVIFIL32.@)
522  */
523 HRESULT WINAPI AVIStreamInfoW(PAVISTREAM pstream, LPAVISTREAMINFOW asi,
524 			      LONG size)
525 {
526   TRACE("(%p,%p,%d)\n", pstream, asi, size);
527 
528   if (pstream == NULL)
529     return AVIERR_BADHANDLE;
530 
531   return IAVIStream_Info(pstream, asi, size);
532 }
533 
534 /***********************************************************************
535  *		AVIStreamFindSample	(AVIFIL32.@)
536  */
537 LONG WINAPI AVIStreamFindSample(PAVISTREAM pstream, LONG pos, LONG flags)
538 {
539   TRACE("(%p,%d,0x%X)\n", pstream, pos, flags);
540 
541   if (pstream == NULL)
542     return -1;
543 
544   return IAVIStream_FindSample(pstream, pos, flags);
545 }
546 
547 /***********************************************************************
548  *		AVIStreamReadFormat	(AVIFIL32.@)
549  */
550 HRESULT WINAPI AVIStreamReadFormat(PAVISTREAM pstream, LONG pos,
551 				   LPVOID format, LPLONG formatsize)
552 {
553   TRACE("(%p,%d,%p,%p)\n", pstream, pos, format, formatsize);
554 
555   if (pstream == NULL)
556     return AVIERR_BADHANDLE;
557 
558   return IAVIStream_ReadFormat(pstream, pos, format, formatsize);
559 }
560 
561 /***********************************************************************
562  *		AVIStreamSetFormat	(AVIFIL32.@)
563  */
564 HRESULT WINAPI AVIStreamSetFormat(PAVISTREAM pstream, LONG pos,
565 				  LPVOID format, LONG formatsize)
566 {
567   TRACE("(%p,%d,%p,%d)\n", pstream, pos, format, formatsize);
568 
569   if (pstream == NULL)
570     return AVIERR_BADHANDLE;
571 
572   return IAVIStream_SetFormat(pstream, pos, format, formatsize);
573 }
574 
575 /***********************************************************************
576  *		AVIStreamRead		(AVIFIL32.@)
577  */
578 HRESULT WINAPI AVIStreamRead(PAVISTREAM pstream, LONG start, LONG samples,
579 			     LPVOID buffer, LONG buffersize,
580 			     LPLONG bytesread, LPLONG samplesread)
581 {
582   TRACE("(%p,%d,%d,%p,%d,%p,%p)\n", pstream, start, samples, buffer,
583 	buffersize, bytesread, samplesread);
584 
585   if (pstream == NULL)
586     return AVIERR_BADHANDLE;
587 
588   return IAVIStream_Read(pstream, start, samples, buffer, buffersize,
589 			 bytesread, samplesread);
590 }
591 
592 /***********************************************************************
593  *		AVIStreamWrite		(AVIFIL32.@)
594  */
595 HRESULT WINAPI AVIStreamWrite(PAVISTREAM pstream, LONG start, LONG samples,
596 			      LPVOID buffer, LONG buffersize, DWORD flags,
597 			      LPLONG sampwritten, LPLONG byteswritten)
598 {
599   TRACE("(%p,%d,%d,%p,%d,0x%X,%p,%p)\n", pstream, start, samples, buffer,
600 	buffersize, flags, sampwritten, byteswritten);
601 
602   if (pstream == NULL)
603     return AVIERR_BADHANDLE;
604 
605   return IAVIStream_Write(pstream, start, samples, buffer, buffersize,
606 			  flags, sampwritten, byteswritten);
607 }
608 
609 /***********************************************************************
610  *		AVIStreamReadData	(AVIFIL32.@)
611  */
612 HRESULT WINAPI AVIStreamReadData(PAVISTREAM pstream, DWORD fcc, LPVOID lp,
613 				 LPLONG lpread)
614 {
615   TRACE("(%p,'%4.4s',%p,%p)\n", pstream, (char*)&fcc, lp, lpread);
616 
617   if (pstream == NULL)
618     return AVIERR_BADHANDLE;
619 
620   return IAVIStream_ReadData(pstream, fcc, lp, lpread);
621 }
622 
623 /***********************************************************************
624  *		AVIStreamWriteData	(AVIFIL32.@)
625  */
626 HRESULT WINAPI AVIStreamWriteData(PAVISTREAM pstream, DWORD fcc, LPVOID lp,
627 				  LONG size)
628 {
629   TRACE("(%p,'%4.4s',%p,%d)\n", pstream, (char*)&fcc, lp, size);
630 
631   if (pstream == NULL)
632     return AVIERR_BADHANDLE;
633 
634   return IAVIStream_WriteData(pstream, fcc, lp, size);
635 }
636 
637 /***********************************************************************
638  *		AVIStreamGetFrameOpen	(AVIFIL32.@)
639  */
640 PGETFRAME WINAPI AVIStreamGetFrameOpen(PAVISTREAM pstream,
641 				       LPBITMAPINFOHEADER lpbiWanted)
642 {
643   PGETFRAME pg = NULL;
644 
645   TRACE("(%p,%p)\n", pstream, lpbiWanted);
646 
647   if (FAILED(IAVIStream_QueryInterface(pstream, &IID_IGetFrame, (LPVOID*)&pg)) ||
648       pg == NULL) {
649     pg = AVIFILE_CreateGetFrame(pstream);
650     if (pg == NULL)
651       return NULL;
652   }
653 
654   if (FAILED(IGetFrame_SetFormat(pg, lpbiWanted, NULL, 0, 0, -1, -1))) {
655     IGetFrame_Release(pg);
656     return NULL;
657   }
658 
659   return pg;
660 }
661 
662 /***********************************************************************
663  *		AVIStreamGetFrame	(AVIFIL32.@)
664  */
665 LPVOID WINAPI AVIStreamGetFrame(PGETFRAME pg, LONG pos)
666 {
667   TRACE("(%p,%d)\n", pg, pos);
668 
669   if (pg == NULL)
670     return NULL;
671 
672   return IGetFrame_GetFrame(pg, pos);
673 }
674 
675 /***********************************************************************
676  *		AVIStreamGetFrameClose	(AVIFIL32.@)
677  */
678 HRESULT WINAPI AVIStreamGetFrameClose(PGETFRAME pg)
679 {
680   TRACE("(%p)\n", pg);
681 
682   if (pg != NULL)
683     return IGetFrame_Release(pg);
684   return 0;
685 }
686 
687 /***********************************************************************
688  *		AVIMakeCompressedStream	(AVIFIL32.@)
689  */
690 HRESULT WINAPI AVIMakeCompressedStream(PAVISTREAM *ppsCompressed,
691 				       PAVISTREAM psSource,
692 				       LPAVICOMPRESSOPTIONS aco,
693 				       LPCLSID pclsidHandler)
694 {
695   AVISTREAMINFOW asiw;
696   CHAR           szRegKey[25];
697   CHAR           szValue[100];
698   CLSID          clsidHandler;
699   HRESULT        hr;
700   LONG           size = sizeof(szValue);
701 
702   TRACE("(%p,%p,%p,%s)\n", ppsCompressed, psSource, aco,
703 	debugstr_guid(pclsidHandler));
704 
705   if (ppsCompressed == NULL)
706     return AVIERR_BADPARAM;
707   if (psSource == NULL)
708     return AVIERR_BADHANDLE;
709 
710   *ppsCompressed = NULL;
711 
712   /* if no handler given get default ones based on streamtype */
713   if (pclsidHandler == NULL) {
714     hr = IAVIStream_Info(psSource, &asiw, sizeof(asiw));
715     if (FAILED(hr))
716       return hr;
717 
718     wsprintfA(szRegKey, "AVIFile\\Compressors\\%4.4s", (char*)&asiw.fccType);
719     if (RegQueryValueA(HKEY_CLASSES_ROOT, szRegKey, szValue, &size) != ERROR_SUCCESS)
720       return AVIERR_UNSUPPORTED;
721     if (AVIFILE_CLSIDFromString(szValue, &clsidHandler) != S_OK)
722       return AVIERR_UNSUPPORTED;
723   } else
724     clsidHandler = *pclsidHandler;
725 
726   hr = CoCreateInstance(&clsidHandler, NULL, CLSCTX_INPROC, &IID_IAVIStream, (LPVOID*)ppsCompressed);
727   if (FAILED(hr) || *ppsCompressed == NULL)
728     return hr;
729 
730   hr = IAVIStream_Create(*ppsCompressed, (LPARAM)psSource, (LPARAM)aco);
731   if (FAILED(hr)) {
732     IAVIStream_Release(*ppsCompressed);
733     *ppsCompressed = NULL;
734   }
735 
736   return hr;
737 }
738 
739 /***********************************************************************
740  *		AVIMakeFileFromStreams	(AVIFIL32.@)
741  */
742 HRESULT WINAPI AVIMakeFileFromStreams(PAVIFILE *ppfile, int nStreams,
743 				      PAVISTREAM *ppStreams)
744 {
745   TRACE("(%p,%d,%p)\n", ppfile, nStreams, ppStreams);
746 
747   if (nStreams < 0 || ppfile == NULL || ppStreams == NULL)
748     return AVIERR_BADPARAM;
749 
750   *ppfile = AVIFILE_CreateAVITempFile(nStreams, ppStreams);
751   if (*ppfile == NULL)
752     return AVIERR_MEMORY;
753 
754   return AVIERR_OK;
755 }
756 
757 /***********************************************************************
758  *		AVIStreamOpenFromFile	(AVIFIL32.@)
759  *		AVIStreamOpenFromFileA	(AVIFIL32.@)
760  */
761 HRESULT WINAPI AVIStreamOpenFromFileA(PAVISTREAM *ppavi, LPCSTR szFile,
762 				      DWORD fccType, LONG lParam,
763 				      UINT mode, LPCLSID pclsidHandler)
764 {
765   PAVIFILE pfile = NULL;
766   HRESULT  hr;
767 
768   TRACE("(%p,%s,'%4.4s',%d,0x%X,%s)\n", ppavi, debugstr_a(szFile),
769 	(char*)&fccType, lParam, mode, debugstr_guid(pclsidHandler));
770 
771   if (ppavi == NULL || szFile == NULL)
772     return AVIERR_BADPARAM;
773 
774   *ppavi = NULL;
775 
776   hr = AVIFileOpenA(&pfile, szFile, mode, pclsidHandler);
777   if (FAILED(hr) || pfile == NULL)
778     return hr;
779 
780   hr = IAVIFile_GetStream(pfile, ppavi, fccType, lParam);
781   IAVIFile_Release(pfile);
782 
783   return hr;
784 }
785 
786 /***********************************************************************
787  *		AVIStreamOpenFromFileW	(AVIFIL32.@)
788  */
789 HRESULT WINAPI AVIStreamOpenFromFileW(PAVISTREAM *ppavi, LPCWSTR szFile,
790 				      DWORD fccType, LONG lParam,
791 				      UINT mode, LPCLSID pclsidHandler)
792 {
793   PAVIFILE pfile = NULL;
794   HRESULT  hr;
795 
796   TRACE("(%p,%s,'%4.4s',%d,0x%X,%s)\n", ppavi, debugstr_w(szFile),
797 	(char*)&fccType, lParam, mode, debugstr_guid(pclsidHandler));
798 
799   if (ppavi == NULL || szFile == NULL)
800     return AVIERR_BADPARAM;
801 
802   *ppavi = NULL;
803 
804   hr = AVIFileOpenW(&pfile, szFile, mode, pclsidHandler);
805   if (FAILED(hr) || pfile == NULL)
806     return hr;
807 
808   hr = IAVIFile_GetStream(pfile, ppavi, fccType, lParam);
809   IAVIFile_Release(pfile);
810 
811   return hr;
812 }
813 
814 /***********************************************************************
815  *		AVIStreamBeginStreaming	(AVIFIL32.@)
816  */
817 LONG WINAPI AVIStreamBeginStreaming(PAVISTREAM pavi, LONG lStart, LONG lEnd, LONG lRate)
818 {
819   IAVIStreaming* pstream = NULL;
820   HRESULT hr;
821 
822   TRACE("(%p,%d,%d,%d)\n", pavi, lStart, lEnd, lRate);
823 
824   if (pavi == NULL)
825     return AVIERR_BADHANDLE;
826 
827   hr = IAVIStream_QueryInterface(pavi, &IID_IAVIStreaming, (LPVOID*)&pstream);
828   if (SUCCEEDED(hr) && pstream != NULL) {
829     hr = IAVIStreaming_Begin(pstream, lStart, lEnd, lRate);
830     IAVIStreaming_Release(pstream);
831   } else
832     hr = AVIERR_OK;
833 
834   return hr;
835 }
836 
837 /***********************************************************************
838  *		AVIStreamEndStreaming	(AVIFIL32.@)
839  */
840 LONG WINAPI AVIStreamEndStreaming(PAVISTREAM pavi)
841 {
842   IAVIStreaming* pstream = NULL;
843   HRESULT hr;
844 
845   TRACE("(%p)\n", pavi);
846 
847   hr = IAVIStream_QueryInterface(pavi, &IID_IAVIStreaming, (LPVOID*)&pstream);
848   if (SUCCEEDED(hr) && pstream != NULL) {
849     IAVIStreaming_End(pstream);
850     IAVIStreaming_Release(pstream);
851   }
852 
853  return AVIERR_OK;
854 }
855 
856 /***********************************************************************
857  *		AVIStreamStart		(AVIFIL32.@)
858  */
859 LONG WINAPI AVIStreamStart(PAVISTREAM pstream)
860 {
861   AVISTREAMINFOW asiw;
862 
863   TRACE("(%p)\n", pstream);
864 
865   if (pstream == NULL)
866     return 0;
867 
868   if (FAILED(IAVIStream_Info(pstream, &asiw, sizeof(asiw))))
869     return 0;
870 
871   return asiw.dwStart;
872 }
873 
874 /***********************************************************************
875  *		AVIStreamLength		(AVIFIL32.@)
876  */
877 LONG WINAPI AVIStreamLength(PAVISTREAM pstream)
878 {
879   AVISTREAMINFOW asiw;
880 
881   TRACE("(%p)\n", pstream);
882 
883   if (pstream == NULL)
884     return 0;
885 
886   if (FAILED(IAVIStream_Info(pstream, &asiw, sizeof(asiw))))
887     return 0;
888 
889   return asiw.dwLength;
890 }
891 
892 /***********************************************************************
893  *		AVIStreamSampleToTime	(AVIFIL32.@)
894  */
895 LONG WINAPI AVIStreamSampleToTime(PAVISTREAM pstream, LONG lSample)
896 {
897   AVISTREAMINFOW asiw;
898   LONG time;
899 
900   TRACE("(%p,%d)\n", pstream, lSample);
901 
902   if (pstream == NULL)
903     return -1;
904 
905   if (FAILED(IAVIStream_Info(pstream, &asiw, sizeof(asiw))))
906     return -1;
907   if (asiw.dwRate == 0)
908     return -1;
909 
910   /* limit to stream bounds */
911   if (lSample < asiw.dwStart)
912     lSample = asiw.dwStart;
913   if (lSample > asiw.dwStart + asiw.dwLength)
914     lSample = asiw.dwStart + asiw.dwLength;
915 
916   if (asiw.dwRate / asiw.dwScale < 1000)
917     time = (LONG)(((float)lSample * asiw.dwScale * 1000) / asiw.dwRate);
918   else
919     time = (LONG)(((float)lSample * asiw.dwScale * 1000 + (asiw.dwRate - 1)) / asiw.dwRate);
920 
921   TRACE(" -> %d\n",time);
922   return time;
923 }
924 
925 /***********************************************************************
926  *		AVIStreamTimeToSample	(AVIFIL32.@)
927  */
928 LONG WINAPI AVIStreamTimeToSample(PAVISTREAM pstream, LONG lTime)
929 {
930   AVISTREAMINFOW asiw;
931   ULONG sample;
932 
933   TRACE("(%p,%d)\n", pstream, lTime);
934 
935   if (pstream == NULL || lTime < 0)
936     return -1;
937 
938   if (FAILED(IAVIStream_Info(pstream, &asiw, sizeof(asiw))))
939     return -1;
940   if (asiw.dwScale == 0)
941     return -1;
942 
943   if (asiw.dwRate / asiw.dwScale < 1000)
944     sample = (LONG)((((float)asiw.dwRate * lTime) / (asiw.dwScale * 1000)));
945   else
946     sample = (LONG)(((float)asiw.dwRate * lTime + (asiw.dwScale * 1000 - 1)) / (asiw.dwScale * 1000));
947 
948   /* limit to stream bounds */
949   if (sample < asiw.dwStart)
950     sample = asiw.dwStart;
951   if (sample > asiw.dwStart + asiw.dwLength)
952     sample = asiw.dwStart + asiw.dwLength;
953 
954   TRACE(" -> %d\n", sample);
955   return sample;
956 }
957 
958 /***********************************************************************
959  *		AVIBuildFilter		(AVIFIL32.@)
960  *		AVIBuildFilterA		(AVIFIL32.@)
961  */
962 HRESULT WINAPI AVIBuildFilterA(LPSTR szFilter, LONG cbFilter, BOOL fSaving)
963 {
964   LPWSTR  wszFilter;
965   HRESULT hr;
966 
967   TRACE("(%p,%d,%d)\n", szFilter, cbFilter, fSaving);
968 
969   /* check parameters */
970   if (szFilter == NULL)
971     return AVIERR_BADPARAM;
972   if (cbFilter < 2)
973     return AVIERR_BADSIZE;
974 
975   szFilter[0] = 0;
976   szFilter[1] = 0;
977 
978   wszFilter = HeapAlloc(GetProcessHeap(), 0, cbFilter * sizeof(WCHAR));
979   if (wszFilter == NULL)
980     return AVIERR_MEMORY;
981 
982   hr = AVIBuildFilterW(wszFilter, cbFilter, fSaving);
983   if (SUCCEEDED(hr)) {
984     WideCharToMultiByte(CP_ACP, 0, wszFilter, cbFilter,
985 			szFilter, cbFilter, NULL, NULL);
986   }
987 
988   HeapFree(GetProcessHeap(), 0, wszFilter);
989 
990   return hr;
991 }
992 
993 /***********************************************************************
994  *		AVIBuildFilterW		(AVIFIL32.@)
995  */
996 HRESULT WINAPI AVIBuildFilterW(LPWSTR szFilter, LONG cbFilter, BOOL fSaving)
997 {
998   static const WCHAR all_files[] = { '*','.','*',0,0 };
999   static const WCHAR szClsid[] = {'C','L','S','I','D',0};
1000   static const WCHAR szExtensionFmt[] = {';','*','.','%','s',0};
1001   static const WCHAR szAVIFileExtensions[] =
1002     {'A','V','I','F','i','l','e','\\','E','x','t','e','n','s','i','o','n','s',0};
1003 
1004   AVIFilter *lp;
1005   WCHAR      szAllFiles[40];
1006   WCHAR      szFileExt[10];
1007   WCHAR      szValue[128];
1008   HKEY       hKey;
1009   DWORD      n, i;
1010   LONG       size;
1011   DWORD      count = 0;
1012 
1013   TRACE("(%p,%d,%d)\n", szFilter, cbFilter, fSaving);
1014 
1015   /* check parameters */
1016   if (szFilter == NULL)
1017     return AVIERR_BADPARAM;
1018   if (cbFilter < 2)
1019     return AVIERR_BADSIZE;
1020 
1021   lp = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, MAX_FILTERS * sizeof(AVIFilter));
1022   if (lp == NULL)
1023     return AVIERR_MEMORY;
1024 
1025   /*
1026    * 1. iterate over HKEY_CLASSES_ROOT\\AVIFile\\Extensions and collect
1027    *    extensions and CLSIDs
1028    * 2. iterate over collected CLSIDs and copy its description and its
1029    *    extensions to szFilter if it fits
1030    *
1031    * First filter is named "All multimedia files" and its filter is a
1032    * collection of all possible extensions except "*.*".
1033    */
1034   if (RegOpenKeyW(HKEY_CLASSES_ROOT, szAVIFileExtensions, &hKey) != ERROR_SUCCESS) {
1035     HeapFree(GetProcessHeap(), 0, lp);
1036     return AVIERR_ERROR;
1037   }
1038   for (n = 0;RegEnumKeyW(hKey, n, szFileExt, sizeof(szFileExt)/sizeof(szFileExt[0])) == ERROR_SUCCESS;n++) {
1039     WCHAR clsidW[40];
1040 
1041     /* get CLSID to extension */
1042     size = sizeof(clsidW);
1043     if (RegQueryValueW(hKey, szFileExt, clsidW, &size) != ERROR_SUCCESS)
1044       break;
1045 
1046     /* search if the CLSID is already known */
1047     for (i = 1; i <= count; i++) {
1048       if (lstrcmpW(lp[i].szClsid, clsidW) == 0)
1049 	break; /* a new one */
1050     }
1051 
1052     if (i == count + 1) {
1053       /* it's a new CLSID */
1054 
1055       /* FIXME: How do we get info's about read/write capabilities? */
1056 
1057       if (count >= MAX_FILTERS) {
1058 	/* try to inform user of our full fixed size table */
1059 	ERR(": More than %d filters found! Adjust MAX_FILTERS in dlls/avifil32/api.c\n", MAX_FILTERS);
1060 	break;
1061       }
1062 
1063       lstrcpyW(lp[i].szClsid, clsidW);
1064 
1065       count++;
1066     }
1067 
1068     /* append extension to the filter */
1069     wsprintfW(szValue, szExtensionFmt, szFileExt);
1070     if (lp[i].szExtensions[0] == 0)
1071       lstrcatW(lp[i].szExtensions, szValue + 1);
1072     else
1073       lstrcatW(lp[i].szExtensions, szValue);
1074 
1075     /* also append to the "all multimedia"-filter */
1076     if (lp[0].szExtensions[0] == 0)
1077       lstrcatW(lp[0].szExtensions, szValue + 1);
1078     else
1079       lstrcatW(lp[0].szExtensions, szValue);
1080   }
1081   RegCloseKey(hKey);
1082 
1083   /* 2. get descriptions for the CLSIDs and fill out szFilter */
1084   if (RegOpenKeyW(HKEY_CLASSES_ROOT, szClsid, &hKey) != ERROR_SUCCESS) {
1085     HeapFree(GetProcessHeap(), 0, lp);
1086     return AVIERR_ERROR;
1087   }
1088   for (n = 0; n <= count; n++) {
1089     /* first the description */
1090     if (n != 0) {
1091       size = sizeof(szValue);
1092       if (RegQueryValueW(hKey, lp[n].szClsid, szValue, &size) == ERROR_SUCCESS) {
1093 	size = lstrlenW(szValue);
1094 	lstrcpynW(szFilter, szValue, cbFilter);
1095       }
1096     } else
1097       size = LoadStringW(AVIFILE_hModule,IDS_ALLMULTIMEDIA,szFilter,cbFilter);
1098 
1099     /* check for enough space */
1100     size++;
1101     if (cbFilter < size + lstrlenW(lp[n].szExtensions) + 2) {
1102       szFilter[0] = 0;
1103       szFilter[1] = 0;
1104       HeapFree(GetProcessHeap(), 0, lp);
1105       RegCloseKey(hKey);
1106       return AVIERR_BUFFERTOOSMALL;
1107     }
1108     cbFilter -= size;
1109     szFilter += size;
1110 
1111     /* and then the filter */
1112     lstrcpynW(szFilter, lp[n].szExtensions, cbFilter);
1113     size = lstrlenW(lp[n].szExtensions) + 1;
1114     cbFilter -= size;
1115     szFilter += size;
1116   }
1117 
1118   RegCloseKey(hKey);
1119   HeapFree(GetProcessHeap(), 0, lp);
1120 
1121   /* add "All files" "*.*" filter if enough space left */
1122   size = LoadStringW(AVIFILE_hModule, IDS_ALLFILES,
1123                      szAllFiles, (sizeof(szAllFiles) - sizeof(all_files))/sizeof(WCHAR)) + 1;
1124   memcpy( szAllFiles + size, all_files, sizeof(all_files) );
1125   size += sizeof(all_files) / sizeof(WCHAR);
1126 
1127   if (cbFilter > size) {
1128     memcpy(szFilter, szAllFiles, size * sizeof(szAllFiles[0]));
1129     return AVIERR_OK;
1130   } else {
1131     szFilter[0] = 0;
1132     return AVIERR_BUFFERTOOSMALL;
1133   }
1134 }
1135 
1136 static BOOL AVISaveOptionsFmtChoose(HWND hWnd)
1137 {
1138   LPAVICOMPRESSOPTIONS pOptions = SaveOpts.ppOptions[SaveOpts.nCurrent];
1139   AVISTREAMINFOW       sInfo;
1140 
1141   TRACE("(%p)\n", hWnd);
1142 
1143   if (pOptions == NULL || SaveOpts.ppavis[SaveOpts.nCurrent] == NULL) {
1144     ERR(": bad state!\n");
1145     return FALSE;
1146   }
1147 
1148   if (FAILED(AVIStreamInfoW(SaveOpts.ppavis[SaveOpts.nCurrent],
1149 			    &sInfo, sizeof(sInfo)))) {
1150     ERR(": AVIStreamInfoW failed!\n");
1151     return FALSE;
1152   }
1153 
1154   if (sInfo.fccType == streamtypeVIDEO) {
1155     COMPVARS cv;
1156     BOOL     ret;
1157 
1158     memset(&cv, 0, sizeof(cv));
1159 
1160     if ((pOptions->dwFlags & AVICOMPRESSF_VALID) == 0) {
1161       memset(pOptions, 0, sizeof(AVICOMPRESSOPTIONS));
1162       pOptions->fccType    = streamtypeVIDEO;
1163       pOptions->fccHandler = comptypeDIB;
1164       pOptions->dwQuality  = (DWORD)ICQUALITY_DEFAULT;
1165     }
1166 
1167     cv.cbSize     = sizeof(cv);
1168     cv.dwFlags    = ICMF_COMPVARS_VALID;
1169     /*cv.fccType    = pOptions->fccType; */
1170     cv.fccHandler = pOptions->fccHandler;
1171     cv.lQ         = pOptions->dwQuality;
1172     cv.lpState    = pOptions->lpParms;
1173     cv.cbState    = pOptions->cbParms;
1174     if (pOptions->dwFlags & AVICOMPRESSF_KEYFRAMES)
1175       cv.lKey = pOptions->dwKeyFrameEvery;
1176     else
1177       cv.lKey = 0;
1178     if (pOptions->dwFlags & AVICOMPRESSF_DATARATE)
1179       cv.lDataRate = pOptions->dwBytesPerSecond / 1024; /* need kBytes */
1180     else
1181       cv.lDataRate = 0;
1182 
1183     ret = ICCompressorChoose(hWnd, SaveOpts.uFlags, NULL,
1184 			     SaveOpts.ppavis[SaveOpts.nCurrent], &cv, NULL);
1185 
1186     if (ret) {
1187       pOptions->fccHandler = cv.fccHandler;
1188       pOptions->lpParms   = cv.lpState;
1189       pOptions->cbParms   = cv.cbState;
1190       pOptions->dwQuality = cv.lQ;
1191       if (cv.lKey != 0) {
1192 	pOptions->dwKeyFrameEvery = cv.lKey;
1193 	pOptions->dwFlags |= AVICOMPRESSF_KEYFRAMES;
1194       } else
1195 	pOptions->dwFlags &= ~AVICOMPRESSF_KEYFRAMES;
1196       if (cv.lDataRate != 0) {
1197 	pOptions->dwBytesPerSecond = cv.lDataRate * 1024; /* need bytes */
1198 	pOptions->dwFlags |= AVICOMPRESSF_DATARATE;
1199       } else
1200 	pOptions->dwFlags &= ~AVICOMPRESSF_DATARATE;
1201       pOptions->dwFlags  |= AVICOMPRESSF_VALID;
1202     }
1203     ICCompressorFree(&cv);
1204 
1205     return ret;
1206   } else if (sInfo.fccType == streamtypeAUDIO) {
1207     ACMFORMATCHOOSEW afmtc;
1208     MMRESULT         ret;
1209     LONG             size;
1210 
1211     /* FIXME: check ACM version -- Which version is needed? */
1212 
1213     memset(&afmtc, 0, sizeof(afmtc));
1214     afmtc.cbStruct  = sizeof(afmtc);
1215     afmtc.fdwStyle  = 0;
1216     afmtc.hwndOwner = hWnd;
1217 
1218     acmMetrics(NULL, ACM_METRIC_MAX_SIZE_FORMAT, &size);
1219     if ((pOptions->cbFormat == 0 || pOptions->lpFormat == NULL) && size != 0) {
1220       pOptions->lpFormat = HeapAlloc(GetProcessHeap(), 0, size);
1221       if (!pOptions->lpFormat) return FALSE;
1222       pOptions->cbFormat = size;
1223     } else if (pOptions->cbFormat < (DWORD)size) {
1224       void *new_buffer = HeapReAlloc(GetProcessHeap(), 0, pOptions->lpFormat, size);
1225       if (!new_buffer) return FALSE;
1226       pOptions->lpFormat = new_buffer;
1227       pOptions->cbFormat = size;
1228     }
1229     afmtc.pwfx  = pOptions->lpFormat;
1230     afmtc.cbwfx = pOptions->cbFormat;
1231 
1232     size = 0;
1233     AVIStreamFormatSize(SaveOpts.ppavis[SaveOpts.nCurrent],
1234 			sInfo.dwStart, &size);
1235     if (size < (LONG)sizeof(PCMWAVEFORMAT))
1236       size = sizeof(PCMWAVEFORMAT);
1237     afmtc.pwfxEnum = HeapAlloc(GetProcessHeap(), 0, size);
1238     if (afmtc.pwfxEnum != NULL) {
1239       AVIStreamReadFormat(SaveOpts.ppavis[SaveOpts.nCurrent],
1240 			  sInfo.dwStart, afmtc.pwfxEnum, &size);
1241       afmtc.fdwEnum = ACM_FORMATENUMF_CONVERT;
1242     }
1243 
1244     ret = acmFormatChooseW(&afmtc);
1245     if (ret == S_OK)
1246       pOptions->dwFlags |= AVICOMPRESSF_VALID;
1247 
1248     HeapFree(GetProcessHeap(), 0, afmtc.pwfxEnum);
1249     return ret == S_OK;
1250   } else {
1251     ERR(": unknown streamtype 0x%08X\n", sInfo.fccType);
1252     return FALSE;
1253   }
1254 }
1255 
1256 static void AVISaveOptionsUpdate(HWND hWnd)
1257 {
1258   static const WCHAR szVideoFmt[]={'%','l','d','x','%','l','d','x','%','d',0};
1259   static const WCHAR szAudioFmt[]={'%','s',' ','%','s',0};
1260 
1261   WCHAR          szFormat[128];
1262   AVISTREAMINFOW sInfo;
1263   LPVOID         lpFormat;
1264   LONG           size;
1265 
1266   TRACE("(%p)\n", hWnd);
1267 
1268   SaveOpts.nCurrent = SendDlgItemMessageW(hWnd,IDC_STREAM,CB_GETCURSEL,0,0);
1269   if (SaveOpts.nCurrent < 0)
1270     return;
1271 
1272   if (FAILED(AVIStreamInfoW(SaveOpts.ppavis[SaveOpts.nCurrent], &sInfo, sizeof(sInfo))))
1273     return;
1274 
1275   AVIStreamFormatSize(SaveOpts.ppavis[SaveOpts.nCurrent],sInfo.dwStart,&size);
1276   if (size > 0) {
1277     szFormat[0] = 0;
1278 
1279     /* read format to build format description string */
1280     lpFormat = HeapAlloc(GetProcessHeap(), 0, size);
1281     if (lpFormat != NULL) {
1282       if (SUCCEEDED(AVIStreamReadFormat(SaveOpts.ppavis[SaveOpts.nCurrent],sInfo.dwStart,lpFormat, &size))) {
1283 	if (sInfo.fccType == streamtypeVIDEO) {
1284 	  LPBITMAPINFOHEADER lpbi = lpFormat;
1285 	  ICINFO icinfo;
1286 
1287 	  wsprintfW(szFormat, szVideoFmt, lpbi->biWidth,
1288 		    lpbi->biHeight, lpbi->biBitCount);
1289 
1290 	  if (lpbi->biCompression != BI_RGB) {
1291 	    HIC    hic;
1292 
1293 	    hic = ICLocate(ICTYPE_VIDEO, sInfo.fccHandler, lpFormat,
1294 			   NULL, ICMODE_DECOMPRESS);
1295 	    if (hic != NULL) {
1296 	      if (ICGetInfo(hic, &icinfo, sizeof(icinfo)) == S_OK)
1297 		lstrcatW(szFormat, icinfo.szDescription);
1298 	      ICClose(hic);
1299 	    }
1300 	  } else {
1301 	    LoadStringW(AVIFILE_hModule, IDS_UNCOMPRESSED,
1302 			icinfo.szDescription,
1303 			sizeof(icinfo.szDescription)/sizeof(icinfo.szDescription[0]));
1304 	    lstrcatW(szFormat, icinfo.szDescription);
1305 	  }
1306 	} else if (sInfo.fccType == streamtypeAUDIO) {
1307 	  ACMFORMATTAGDETAILSW aftd;
1308 	  ACMFORMATDETAILSW    afd;
1309 
1310 	  memset(&aftd, 0, sizeof(aftd));
1311 	  memset(&afd, 0, sizeof(afd));
1312 
1313 	  aftd.cbStruct     = sizeof(aftd);
1314 	  aftd.dwFormatTag  = afd.dwFormatTag =
1315 	    ((PWAVEFORMATEX)lpFormat)->wFormatTag;
1316 	  aftd.cbFormatSize = afd.cbwfx = size;
1317 
1318 	  afd.cbStruct      = sizeof(afd);
1319 	  afd.pwfx          = lpFormat;
1320 
1321 	  if (acmFormatTagDetailsW(NULL, &aftd,
1322 				   ACM_FORMATTAGDETAILSF_FORMATTAG) == S_OK) {
1323 	    if (acmFormatDetailsW(NULL,&afd,ACM_FORMATDETAILSF_FORMAT) == S_OK)
1324 	      wsprintfW(szFormat, szAudioFmt, afd.szFormat, aftd.szFormatTag);
1325 	  }
1326 	}
1327       }
1328       HeapFree(GetProcessHeap(), 0, lpFormat);
1329     }
1330 
1331     /* set text for format description */
1332     SetDlgItemTextW(hWnd, IDC_FORMATTEXT, szFormat);
1333 
1334     /* Disable option button for unsupported streamtypes */
1335     if (sInfo.fccType == streamtypeVIDEO ||
1336 	sInfo.fccType == streamtypeAUDIO)
1337       EnableWindow(GetDlgItem(hWnd, IDC_OPTIONS), TRUE);
1338     else
1339       EnableWindow(GetDlgItem(hWnd, IDC_OPTIONS), FALSE);
1340   }
1341 
1342 }
1343 
1344 static INT_PTR CALLBACK AVISaveOptionsDlgProc(HWND hWnd, UINT uMsg,
1345                                               WPARAM wParam, LPARAM lParam)
1346 {
1347   DWORD dwInterleave;
1348   BOOL  bIsInterleaved;
1349   INT   n;
1350 
1351   /*TRACE("(%p,%u,0x%04X,0x%08lX)\n", hWnd, uMsg, wParam, lParam);*/
1352 
1353   switch (uMsg) {
1354   case WM_INITDIALOG:
1355     SaveOpts.nCurrent = 0;
1356     if (SaveOpts.nStreams == 1) {
1357       EndDialog(hWnd, AVISaveOptionsFmtChoose(hWnd));
1358       return TRUE;
1359     }
1360 
1361     /* add streams */
1362     for (n = 0; n < SaveOpts.nStreams; n++) {
1363       AVISTREAMINFOW sInfo;
1364 
1365       AVIStreamInfoW(SaveOpts.ppavis[n], &sInfo, sizeof(sInfo));
1366       SendDlgItemMessageW(hWnd, IDC_STREAM, CB_ADDSTRING,
1367 			  0L, (LPARAM)sInfo.szName);
1368     }
1369 
1370     /* select first stream */
1371     SendDlgItemMessageW(hWnd, IDC_STREAM, CB_SETCURSEL, 0, 0);
1372     SendMessageW(hWnd, WM_COMMAND, MAKELONG(IDC_STREAM, CBN_SELCHANGE), (LPARAM)hWnd);
1373 
1374     /* initialize interleave */
1375     if (SaveOpts.ppOptions[0] != NULL &&
1376 	(SaveOpts.ppOptions[0]->dwFlags & AVICOMPRESSF_VALID)) {
1377       bIsInterleaved = (SaveOpts.ppOptions[0]->dwFlags & AVICOMPRESSF_INTERLEAVE);
1378       dwInterleave = SaveOpts.ppOptions[0]->dwInterleaveEvery;
1379     } else {
1380       bIsInterleaved = TRUE;
1381       dwInterleave   = 0;
1382     }
1383     CheckDlgButton(hWnd, IDC_INTERLEAVE, bIsInterleaved);
1384     SetDlgItemInt(hWnd, IDC_INTERLEAVEEVERY, dwInterleave, FALSE);
1385     EnableWindow(GetDlgItem(hWnd, IDC_INTERLEAVEEVERY), bIsInterleaved);
1386     break;
1387   case WM_COMMAND:
1388     switch (LOWORD(wParam)) {
1389     case IDOK:
1390       /* get data from controls and save them */
1391       dwInterleave   = GetDlgItemInt(hWnd, IDC_INTERLEAVEEVERY, NULL, 0);
1392       bIsInterleaved = IsDlgButtonChecked(hWnd, IDC_INTERLEAVE);
1393       for (n = 0; n < SaveOpts.nStreams; n++) {
1394 	if (SaveOpts.ppOptions[n] != NULL) {
1395 	  if (bIsInterleaved) {
1396 	    SaveOpts.ppOptions[n]->dwFlags |= AVICOMPRESSF_INTERLEAVE;
1397 	    SaveOpts.ppOptions[n]->dwInterleaveEvery = dwInterleave;
1398 	  } else
1399 	    SaveOpts.ppOptions[n]->dwFlags &= ~AVICOMPRESSF_INTERLEAVE;
1400 	}
1401       }
1402       /* fall through */
1403     case IDCANCEL:
1404       EndDialog(hWnd, LOWORD(wParam) == IDOK);
1405       break;
1406     case IDC_INTERLEAVE:
1407       EnableWindow(GetDlgItem(hWnd, IDC_INTERLEAVEEVERY),
1408 		   IsDlgButtonChecked(hWnd, IDC_INTERLEAVE));
1409       break;
1410     case IDC_STREAM:
1411       if (HIWORD(wParam) == CBN_SELCHANGE) {
1412 	/* update control elements */
1413 	AVISaveOptionsUpdate(hWnd);
1414       }
1415       break;
1416     case IDC_OPTIONS:
1417       AVISaveOptionsFmtChoose(hWnd);
1418       break;
1419     };
1420     return TRUE;
1421   };
1422 
1423   return FALSE;
1424 }
1425 
1426 /***********************************************************************
1427  *		AVISaveOptions		(AVIFIL32.@)
1428  */
1429 BOOL WINAPI AVISaveOptions(HWND hWnd, UINT uFlags, INT nStreams,
1430 			   PAVISTREAM *ppavi, LPAVICOMPRESSOPTIONS *ppOptions)
1431 {
1432   LPAVICOMPRESSOPTIONS pSavedOptions = NULL;
1433   INT ret, n;
1434 
1435   TRACE("(%p,0x%X,%d,%p,%p)\n", hWnd, uFlags, nStreams,
1436 	ppavi, ppOptions);
1437 
1438   /* check parameters */
1439   if (nStreams <= 0 || ppavi == NULL || ppOptions == NULL)
1440     return AVIERR_BADPARAM;
1441 
1442   /* save options in case the user presses cancel */
1443   if (nStreams > 1) {
1444     pSavedOptions = HeapAlloc(GetProcessHeap(), 0, nStreams * sizeof(AVICOMPRESSOPTIONS));
1445     if (pSavedOptions == NULL)
1446       return FALSE;
1447 
1448     for (n = 0; n < nStreams; n++) {
1449       if (ppOptions[n] != NULL)
1450 	memcpy(pSavedOptions + n, ppOptions[n], sizeof(AVICOMPRESSOPTIONS));
1451     }
1452   }
1453 
1454   SaveOpts.uFlags    = uFlags;
1455   SaveOpts.nStreams  = nStreams;
1456   SaveOpts.ppavis    = ppavi;
1457   SaveOpts.ppOptions = ppOptions;
1458 
1459   ret = DialogBoxW(AVIFILE_hModule, MAKEINTRESOURCEW(IDD_SAVEOPTIONS),
1460 		   hWnd, AVISaveOptionsDlgProc);
1461 
1462   if (ret == -1)
1463     ret = FALSE;
1464 
1465   /* restore options when user pressed cancel */
1466   if (pSavedOptions != NULL) {
1467     if (ret == FALSE) {
1468       for (n = 0; n < nStreams; n++) {
1469 	if (ppOptions[n] != NULL)
1470 	  memcpy(ppOptions[n], pSavedOptions + n, sizeof(AVICOMPRESSOPTIONS));
1471       }
1472     }
1473     HeapFree(GetProcessHeap(), 0, pSavedOptions);
1474   }
1475 
1476   return ret;
1477 }
1478 
1479 /***********************************************************************
1480  *		AVISaveOptionsFree	(AVIFIL32.@)
1481  */
1482 HRESULT WINAPI AVISaveOptionsFree(INT nStreams,LPAVICOMPRESSOPTIONS*ppOptions)
1483 {
1484   TRACE("(%d,%p)\n", nStreams, ppOptions);
1485 
1486   if (nStreams < 0 || ppOptions == NULL)
1487     return AVIERR_BADPARAM;
1488 
1489   for (nStreams--; nStreams >= 0; nStreams--) {
1490     if (ppOptions[nStreams] != NULL) {
1491       ppOptions[nStreams]->dwFlags &= ~AVICOMPRESSF_VALID;
1492 
1493       if (ppOptions[nStreams]->lpParms != NULL) {
1494 	HeapFree(GetProcessHeap(), 0, ppOptions[nStreams]->lpParms);
1495 	ppOptions[nStreams]->lpParms = NULL;
1496 	ppOptions[nStreams]->cbParms = 0;
1497       }
1498       if (ppOptions[nStreams]->lpFormat != NULL) {
1499 	HeapFree(GetProcessHeap(), 0, ppOptions[nStreams]->lpFormat);
1500 	ppOptions[nStreams]->lpFormat = NULL;
1501 	ppOptions[nStreams]->cbFormat = 0;
1502       }
1503     }
1504   }
1505 
1506   return AVIERR_OK;
1507 }
1508 
1509 /***********************************************************************
1510  *		AVISaveVA		(AVIFIL32.@)
1511  */
1512 HRESULT WINAPI AVISaveVA(LPCSTR szFile, CLSID *pclsidHandler,
1513 			 AVISAVECALLBACK lpfnCallback, int nStream,
1514 			 PAVISTREAM *ppavi, LPAVICOMPRESSOPTIONS *plpOptions)
1515 {
1516   LPWSTR  wszFile = NULL;
1517   HRESULT hr;
1518   int     len;
1519 
1520   TRACE("(%s,%p,%p,%d,%p,%p)\n", debugstr_a(szFile), pclsidHandler,
1521 	lpfnCallback, nStream, ppavi, plpOptions);
1522 
1523   if (szFile == NULL || ppavi == NULL || plpOptions == NULL)
1524     return AVIERR_BADPARAM;
1525 
1526   /* convert ASCII string to Unicode and call Unicode function */
1527   len = MultiByteToWideChar(CP_ACP, 0, szFile, -1, NULL, 0);
1528   if (len <= 0)
1529     return AVIERR_BADPARAM;
1530 
1531   wszFile = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1532   if (wszFile == NULL)
1533     return AVIERR_MEMORY;
1534 
1535   MultiByteToWideChar(CP_ACP, 0, szFile, -1, wszFile, len);
1536 
1537   hr = AVISaveVW(wszFile, pclsidHandler, lpfnCallback,
1538 		 nStream, ppavi, plpOptions);
1539 
1540   HeapFree(GetProcessHeap(), 0, wszFile);
1541 
1542   return hr;
1543 }
1544 
1545 /***********************************************************************
1546  *		AVIFILE_AVISaveDefaultCallback	(internal)
1547  */
1548 static BOOL WINAPI AVIFILE_AVISaveDefaultCallback(INT progress)
1549 {
1550   TRACE("(%d)\n", progress);
1551 
1552   return FALSE;
1553 }
1554 
1555 /***********************************************************************
1556  *		AVISaveVW		(AVIFIL32.@)
1557  */
1558 HRESULT WINAPI AVISaveVW(LPCWSTR szFile, CLSID *pclsidHandler,
1559 			 AVISAVECALLBACK lpfnCallback, int nStreams,
1560 			 PAVISTREAM *ppavi, LPAVICOMPRESSOPTIONS *plpOptions)
1561 {
1562   LONG           lStart[MAX_AVISTREAMS];
1563   PAVISTREAM     pOutStreams[MAX_AVISTREAMS];
1564   PAVISTREAM     pInStreams[MAX_AVISTREAMS];
1565   AVIFILEINFOW   fInfo;
1566   AVISTREAMINFOW sInfo;
1567 
1568   PAVIFILE       pfile = NULL; /* the output AVI file */
1569   LONG           lFirstVideo = -1;
1570   int            curStream;
1571 
1572   /* for interleaving ... */
1573   DWORD          dwInterleave = 0; /* interleave rate */
1574   DWORD          dwFileInitialFrames;
1575   LONG           lFileLength;
1576   LONG           lSampleInc;
1577 
1578   /* for reading/writing the data ... */
1579   LPVOID         lpBuffer = NULL;
1580   LONG           cbBuffer;        /* real size of lpBuffer */
1581   LONG           lBufferSize;     /* needed bytes for format(s), etc. */
1582   LONG           lReadBytes;
1583   LONG           lReadSamples;
1584   HRESULT        hres;
1585 
1586   TRACE("(%s,%p,%p,%d,%p,%p)\n", debugstr_w(szFile), pclsidHandler,
1587 	lpfnCallback, nStreams, ppavi, plpOptions);
1588 
1589   if (szFile == NULL || ppavi == NULL || plpOptions == NULL)
1590     return AVIERR_BADPARAM;
1591   if (nStreams >= MAX_AVISTREAMS) {
1592     WARN("Can't write AVI with %d streams only supports %d -- change MAX_AVISTREAMS!\n", nStreams, MAX_AVISTREAMS);
1593     return AVIERR_INTERNAL;
1594   }
1595 
1596   if (lpfnCallback == NULL)
1597     lpfnCallback = AVIFILE_AVISaveDefaultCallback;
1598 
1599   /* clear local variable(s) */
1600   for (curStream = 0; curStream < nStreams; curStream++) {
1601     pInStreams[curStream]  = NULL;
1602     pOutStreams[curStream] = NULL;
1603   }
1604 
1605   /* open output AVI file (create it if it doesn't exist) */
1606   hres = AVIFileOpenW(&pfile, szFile, OF_CREATE|OF_SHARE_EXCLUSIVE|OF_WRITE,
1607 		      pclsidHandler);
1608   if (FAILED(hres))
1609     return hres;
1610   AVIFileInfoW(pfile, &fInfo, sizeof(fInfo)); /* for dwCaps */
1611 
1612   /* initialize our data structures part 1 */
1613   for (curStream = 0; curStream < nStreams; curStream++) {
1614     PAVISTREAM pCurStream = ppavi[curStream];
1615 
1616     hres = AVIStreamInfoW(pCurStream, &sInfo, sizeof(sInfo));
1617     if (FAILED(hres))
1618       goto error;
1619 
1620     /* search first video stream and check for interleaving */
1621     if (sInfo.fccType == streamtypeVIDEO) {
1622       /* remember first video stream -- needed for interleaving */
1623       if (lFirstVideo < 0)
1624 	lFirstVideo = curStream;
1625     } else if (!dwInterleave) {
1626       /* check if any non-video stream wants to be interleaved */
1627       WARN("options.flags=0x%X options.dwInterleave=%u\n",plpOptions[curStream]->dwFlags,plpOptions[curStream]->dwInterleaveEvery);
1628       if (plpOptions[curStream] != NULL &&
1629 	  plpOptions[curStream]->dwFlags & AVICOMPRESSF_INTERLEAVE)
1630 	dwInterleave = plpOptions[curStream]->dwInterleaveEvery;
1631     }
1632 
1633     /* create de-/compressed stream interface if needed */
1634     pInStreams[curStream] = NULL;
1635     if (plpOptions[curStream] != NULL) {
1636       if (plpOptions[curStream]->fccHandler ||
1637 	  plpOptions[curStream]->lpFormat != NULL) {
1638 	DWORD dwKeySave = plpOptions[curStream]->dwKeyFrameEvery;
1639 
1640 	if (fInfo.dwCaps & AVIFILECAPS_ALLKEYFRAMES)
1641 	  plpOptions[curStream]->dwKeyFrameEvery = 1;
1642 
1643 	hres = AVIMakeCompressedStream(&pInStreams[curStream], pCurStream,
1644 				       plpOptions[curStream], NULL);
1645 	plpOptions[curStream]->dwKeyFrameEvery = dwKeySave;
1646 	if (FAILED(hres) || pInStreams[curStream] == NULL) {
1647 	  pInStreams[curStream] = NULL;
1648 	  goto error;
1649 	}
1650 
1651 	/* test stream interface and update stream-info */
1652 	hres = AVIStreamInfoW(pInStreams[curStream], &sInfo, sizeof(sInfo));
1653 	if (FAILED(hres))
1654 	  goto error;
1655       }
1656     }
1657 
1658     /* now handle streams which will only be copied */
1659     if (pInStreams[curStream] == NULL) {
1660       pCurStream = pInStreams[curStream] = ppavi[curStream];
1661       AVIStreamAddRef(pCurStream);
1662     } else
1663       pCurStream = pInStreams[curStream];
1664 
1665     lStart[curStream] = sInfo.dwStart;
1666   } /* for all streams */
1667 
1668   /* check that first video stream is the first stream */
1669   if (lFirstVideo > 0) {
1670     PAVISTREAM pTmp = pInStreams[lFirstVideo];
1671     LONG lTmp = lStart[lFirstVideo];
1672 
1673     pInStreams[lFirstVideo] = pInStreams[0];
1674     pInStreams[0] = pTmp;
1675     lStart[lFirstVideo] = lStart[0];
1676     lStart[0] = lTmp;
1677     lFirstVideo = 0;
1678   }
1679 
1680   /* allocate buffer for formats, data, etc. of an initial size of 64 kBytes*/
1681   cbBuffer = 0x00010000;
1682   lpBuffer = HeapAlloc(GetProcessHeap(), 0, cbBuffer);
1683   if (lpBuffer == NULL) {
1684     hres = AVIERR_MEMORY;
1685     goto error;
1686   }
1687 
1688   AVIStreamInfoW(pInStreams[0], &sInfo, sizeof(sInfo));
1689   lFileLength = sInfo.dwLength;
1690   dwFileInitialFrames = 0;
1691   if (lFirstVideo >= 0) {
1692     /* check for correct version of the format
1693      *  -- need at least BITMAPINFOHEADER or newer
1694      */
1695     lSampleInc = 1;
1696     lBufferSize = cbBuffer;
1697     hres = AVIStreamReadFormat(pInStreams[lFirstVideo], AVIStreamStart(pInStreams[lFirstVideo]), lpBuffer, &lBufferSize);
1698     if (lBufferSize < (LONG)sizeof(BITMAPINFOHEADER))
1699       hres = AVIERR_INTERNAL;
1700     if (FAILED(hres))
1701       goto error;
1702   } else /* use one second blocks for interleaving if no video present */
1703     lSampleInc = AVIStreamTimeToSample(pInStreams[0], 1000000);
1704 
1705   /* create output streams */
1706   for (curStream = 0; curStream < nStreams; curStream++) {
1707     AVIStreamInfoW(pInStreams[curStream], &sInfo, sizeof(sInfo));
1708 
1709     sInfo.dwInitialFrames = 0;
1710     if (dwInterleave != 0 && curStream > 0 && sInfo.fccType != streamtypeVIDEO) {
1711       /* 750 ms initial frames for non-video streams */
1712       sInfo.dwInitialFrames = AVIStreamTimeToSample(pInStreams[0], 750);
1713     }
1714 
1715     hres = AVIFileCreateStreamW(pfile, &pOutStreams[curStream], &sInfo);
1716     if (pOutStreams[curStream] != NULL && SUCCEEDED(hres)) {
1717       /* copy initial format for this stream */
1718       lBufferSize = cbBuffer;
1719       hres = AVIStreamReadFormat(pInStreams[curStream], sInfo.dwStart,
1720 				 lpBuffer, &lBufferSize);
1721       if (FAILED(hres))
1722 	goto error;
1723       hres = AVIStreamSetFormat(pOutStreams[curStream], 0, lpBuffer, lBufferSize);
1724       if (FAILED(hres))
1725 	goto error;
1726 
1727       /* try to copy stream handler data */
1728       lBufferSize = cbBuffer;
1729       hres = AVIStreamReadData(pInStreams[curStream], ckidSTREAMHANDLERDATA,
1730 			       lpBuffer, &lBufferSize);
1731       if (SUCCEEDED(hres) && lBufferSize > 0) {
1732 	hres = AVIStreamWriteData(pOutStreams[curStream],ckidSTREAMHANDLERDATA,
1733 				  lpBuffer, lBufferSize);
1734 	if (FAILED(hres))
1735 	  goto error;
1736       }
1737 
1738       if (dwFileInitialFrames < sInfo.dwInitialFrames)
1739 	dwFileInitialFrames = sInfo.dwInitialFrames;
1740       lReadBytes =
1741 	AVIStreamSampleToSample(pOutStreams[0], pInStreams[curStream],
1742 				sInfo.dwLength);
1743       if (lFileLength < lReadBytes)
1744 	lFileLength = lReadBytes;
1745     } else {
1746       /* creation of de-/compression stream interface failed */
1747       WARN("creation of (de-)compression stream failed for stream %d\n",curStream);
1748       AVIStreamRelease(pInStreams[curStream]);
1749       if (curStream + 1 >= nStreams) {
1750 	/* move the others one up */
1751 	PAVISTREAM *ppas = &pInStreams[curStream];
1752 	int            n = nStreams - (curStream + 1);
1753 
1754 	do {
1755 	  *ppas = pInStreams[curStream + 1];
1756 	} while (--n);
1757       }
1758       nStreams--;
1759       curStream--;
1760     }
1761   } /* create output streams for all input streams */
1762 
1763   /* have we still something to write, or lost everything? */
1764   if (nStreams <= 0)
1765     goto error;
1766 
1767   if (dwInterleave) {
1768     LONG lCurFrame = -dwFileInitialFrames;
1769 
1770     /* interleaved file */
1771     if (dwInterleave == 1)
1772       AVIFileEndRecord(pfile);
1773 
1774     for (; lCurFrame < lFileLength; lCurFrame += lSampleInc) {
1775       for (curStream = 0; curStream < nStreams; curStream++) {
1776 	LONG lLastSample;
1777 
1778 	hres = AVIStreamInfoW(pOutStreams[curStream], &sInfo, sizeof(sInfo));
1779 	if (FAILED(hres))
1780 	  goto error;
1781 
1782 	/* initial frames phase at the end for this stream? */
1783 	if (-(LONG)sInfo.dwInitialFrames > lCurFrame)
1784 	  continue;
1785 
1786 	if ((lFileLength - lSampleInc) <= lCurFrame) {
1787 	  lLastSample = AVIStreamLength(pInStreams[curStream]);
1788 	  lFirstVideo = lLastSample + AVIStreamStart(pInStreams[curStream]);
1789 	} else {
1790 	  if (curStream != 0) {
1791 	    lFirstVideo =
1792 	      AVIStreamSampleToSample(pInStreams[curStream], pInStreams[0],
1793 				      (sInfo.fccType == streamtypeVIDEO ?
1794 				       (LONG)dwInterleave : lSampleInc) +
1795 				      sInfo.dwInitialFrames + lCurFrame);
1796 	  } else
1797 	    lFirstVideo = lSampleInc + (sInfo.dwInitialFrames + lCurFrame);
1798 
1799 	  lLastSample = AVIStreamEnd(pInStreams[curStream]);
1800 	  if (lLastSample <= lFirstVideo)
1801 	    lFirstVideo = lLastSample;
1802 	}
1803 
1804 	/* copy needed samples now */
1805 	WARN("copy from stream %d samples %d to %d...\n",curStream,
1806 	      lStart[curStream],lFirstVideo);
1807 	while (lFirstVideo > lStart[curStream]) {
1808 	  DWORD flags = 0;
1809 
1810 	  /* copy format in case it can change */
1811 	  lBufferSize = cbBuffer;
1812 	  hres = AVIStreamReadFormat(pInStreams[curStream], lStart[curStream],
1813 				     lpBuffer, &lBufferSize);
1814 	  if (FAILED(hres))
1815 	    goto error;
1816 	  AVIStreamSetFormat(pOutStreams[curStream], lStart[curStream],
1817 			     lpBuffer, lBufferSize);
1818 
1819 	  /* try to read data until we got it, or error */
1820 	  do {
1821 	    hres = AVIStreamRead(pInStreams[curStream], lStart[curStream],
1822 				 lFirstVideo - lStart[curStream], lpBuffer,
1823 				 cbBuffer, &lReadBytes, &lReadSamples);
1824 	  } while ((hres == AVIERR_BUFFERTOOSMALL) &&
1825 		   (lpBuffer = HeapReAlloc(GetProcessHeap(), 0, lpBuffer, cbBuffer *= 2)) != NULL);
1826 	  if (lpBuffer == NULL)
1827 	    hres = AVIERR_MEMORY;
1828 	  if (FAILED(hres))
1829 	    goto error;
1830 
1831 	  if (AVIStreamIsKeyFrame(pInStreams[curStream], (LONG)sInfo.dwStart))
1832 	    flags = AVIIF_KEYFRAME;
1833 	  hres = AVIStreamWrite(pOutStreams[curStream], -1, lReadSamples,
1834 				lpBuffer, lReadBytes, flags, NULL, NULL);
1835 	  if (FAILED(hres))
1836 	    goto error;
1837 
1838 	  lStart[curStream] += lReadSamples;
1839 	}
1840 	lStart[curStream] = lFirstVideo;
1841       } /* stream by stream */
1842 
1843       /* need to close this block? */
1844       if (dwInterleave == 1) {
1845 	hres = AVIFileEndRecord(pfile);
1846 	if (FAILED(hres))
1847 	  break;
1848       }
1849 
1850       /* show progress */
1851       if (lpfnCallback(MulDiv(dwFileInitialFrames + lCurFrame, 100,
1852 			      dwFileInitialFrames + lFileLength))) {
1853 	hres = AVIERR_USERABORT;
1854 	break;
1855       }
1856     } /* copy frame by frame */
1857   } else {
1858     /* non-interleaved file */
1859 
1860     for (curStream = 0; curStream < nStreams; curStream++) {
1861       /* show progress */
1862       if (lpfnCallback(MulDiv(curStream, 100, nStreams))) {
1863 	hres = AVIERR_USERABORT;
1864 	goto error;
1865       }
1866 
1867       AVIStreamInfoW(pInStreams[curStream], &sInfo, sizeof(sInfo));
1868 
1869       if (sInfo.dwSampleSize != 0) {
1870 	/* sample-based data like audio */
1871 	while (sInfo.dwStart < sInfo.dwLength) {
1872 	  LONG lSamples = cbBuffer / sInfo.dwSampleSize;
1873 
1874 	  /* copy format in case it can change */
1875 	  lBufferSize = cbBuffer;
1876 	  hres = AVIStreamReadFormat(pInStreams[curStream], sInfo.dwStart,
1877 				     lpBuffer, &lBufferSize);
1878 	  if (FAILED(hres))
1879 	    goto error;
1880 	  AVIStreamSetFormat(pOutStreams[curStream], sInfo.dwStart,
1881 			     lpBuffer, lBufferSize);
1882 
1883 	  /* limit to stream boundaries */
1884 	  if (lSamples != (LONG)(sInfo.dwLength - sInfo.dwStart))
1885 	    lSamples = sInfo.dwLength - sInfo.dwStart;
1886 
1887 	  /* now try to read until we get it, or an error occurs */
1888 	  do {
1889 	    lReadBytes   = cbBuffer;
1890 	    lReadSamples = 0;
1891 	    hres = AVIStreamRead(pInStreams[curStream],sInfo.dwStart,lSamples,
1892 				 lpBuffer,cbBuffer,&lReadBytes,&lReadSamples);
1893 	  } while ((hres == AVIERR_BUFFERTOOSMALL) &&
1894 		   (lpBuffer = HeapReAlloc(GetProcessHeap(), 0, lpBuffer, cbBuffer *= 2)) != NULL);
1895 	  if (lpBuffer == NULL)
1896 	    hres = AVIERR_MEMORY;
1897 	  if (FAILED(hres))
1898 	    goto error;
1899 	  if (lReadSamples != 0) {
1900 	    sInfo.dwStart += lReadSamples;
1901 	    hres = AVIStreamWrite(pOutStreams[curStream], -1, lReadSamples,
1902 				  lpBuffer, lReadBytes, 0, NULL , NULL);
1903 	    if (FAILED(hres))
1904 	      goto error;
1905 
1906 	    /* show progress */
1907 	    if (lpfnCallback(MulDiv(sInfo.dwStart,100,nStreams*sInfo.dwLength)+
1908 			     MulDiv(curStream, 100, nStreams))) {
1909 	      hres = AVIERR_USERABORT;
1910 	      goto error;
1911 	    }
1912 	  } else {
1913 	    if ((sInfo.dwLength - sInfo.dwStart) != 1) {
1914 	      hres = AVIERR_FILEREAD;
1915 	      goto error;
1916 	    }
1917 	  }
1918 	}
1919       } else {
1920 	/* block-based data like video */
1921 	for (; sInfo.dwStart < sInfo.dwLength; sInfo.dwStart++) {
1922 	  DWORD flags = 0;
1923 
1924 	  /* copy format in case it can change */
1925 	  lBufferSize = cbBuffer;
1926 	  hres = AVIStreamReadFormat(pInStreams[curStream], sInfo.dwStart,
1927 				     lpBuffer, &lBufferSize);
1928 	  if (FAILED(hres))
1929 	    goto error;
1930 	  AVIStreamSetFormat(pOutStreams[curStream], sInfo.dwStart,
1931 			     lpBuffer, lBufferSize);
1932 
1933 	  /* try to read block and resize buffer if necessary */
1934 	  do {
1935 	    lReadSamples = 0;
1936 	    lReadBytes   = cbBuffer;
1937 	    hres = AVIStreamRead(pInStreams[curStream], sInfo.dwStart, 1,
1938 				 lpBuffer, cbBuffer,&lReadBytes,&lReadSamples);
1939 	  } while ((hres == AVIERR_BUFFERTOOSMALL) &&
1940 		   (lpBuffer = HeapReAlloc(GetProcessHeap(), 0, lpBuffer, cbBuffer *= 2)) != NULL);
1941 	  if (lpBuffer == NULL)
1942 	    hres = AVIERR_MEMORY;
1943 	  if (FAILED(hres))
1944 	    goto error;
1945 	  if (lReadSamples != 1) {
1946 	    hres = AVIERR_FILEREAD;
1947 	    goto error;
1948 	  }
1949 
1950 	  if (AVIStreamIsKeyFrame(pInStreams[curStream], (LONG)sInfo.dwStart))
1951 	    flags = AVIIF_KEYFRAME;
1952 	  hres = AVIStreamWrite(pOutStreams[curStream], -1, lReadSamples,
1953 				lpBuffer, lReadBytes, flags, NULL, NULL);
1954 	  if (FAILED(hres))
1955 	    goto error;
1956 
1957 	  /* show progress */
1958 	  if (lpfnCallback(MulDiv(sInfo.dwStart,100,nStreams*sInfo.dwLength)+
1959 			   MulDiv(curStream, 100, nStreams))) {
1960 	    hres = AVIERR_USERABORT;
1961 	    goto error;
1962 	  }
1963 	} /* copy all blocks */
1964       }
1965     } /* copy data stream by stream */
1966   }
1967 
1968  error:
1969   HeapFree(GetProcessHeap(), 0, lpBuffer);
1970   if (pfile != NULL) {
1971     for (curStream = 0; curStream < nStreams; curStream++) {
1972       if (pOutStreams[curStream] != NULL)
1973 	AVIStreamRelease(pOutStreams[curStream]);
1974       if (pInStreams[curStream] != NULL)
1975 	AVIStreamRelease(pInStreams[curStream]);
1976     }
1977 
1978     AVIFileRelease(pfile);
1979   }
1980 
1981   return hres;
1982 }
1983 
1984 /***********************************************************************
1985  *		EditStreamClone		(AVIFIL32.@)
1986  */
1987 HRESULT WINAPI EditStreamClone(PAVISTREAM pStream, PAVISTREAM *ppResult)
1988 {
1989   PAVIEDITSTREAM pEdit = NULL;
1990   HRESULT        hr;
1991 
1992   TRACE("(%p,%p)\n", pStream, ppResult);
1993 
1994   if (pStream == NULL)
1995     return AVIERR_BADHANDLE;
1996   if (ppResult == NULL)
1997     return AVIERR_BADPARAM;
1998 
1999   *ppResult = NULL;
2000 
2001   hr = IAVIStream_QueryInterface(pStream, &IID_IAVIEditStream,(LPVOID*)&pEdit);
2002   if (SUCCEEDED(hr) && pEdit != NULL) {
2003     hr = IAVIEditStream_Clone(pEdit, ppResult);
2004 
2005     IAVIEditStream_Release(pEdit);
2006   } else
2007     hr = AVIERR_UNSUPPORTED;
2008 
2009   return hr;
2010 }
2011 
2012 /***********************************************************************
2013  *		EditStreamCopy		(AVIFIL32.@)
2014  */
2015 HRESULT WINAPI EditStreamCopy(PAVISTREAM pStream, LONG *plStart,
2016 			      LONG *plLength, PAVISTREAM *ppResult)
2017 {
2018   PAVIEDITSTREAM pEdit = NULL;
2019   HRESULT        hr;
2020 
2021   TRACE("(%p,%p,%p,%p)\n", pStream, plStart, plLength, ppResult);
2022 
2023   if (pStream == NULL)
2024     return AVIERR_BADHANDLE;
2025   if (plStart == NULL || plLength == NULL || ppResult == NULL)
2026     return AVIERR_BADPARAM;
2027 
2028   *ppResult = NULL;
2029 
2030   hr = IAVIStream_QueryInterface(pStream, &IID_IAVIEditStream,(LPVOID*)&pEdit);
2031   if (SUCCEEDED(hr) && pEdit != NULL) {
2032     hr = IAVIEditStream_Copy(pEdit, plStart, plLength, ppResult);
2033 
2034     IAVIEditStream_Release(pEdit);
2035   } else
2036     hr = AVIERR_UNSUPPORTED;
2037 
2038   return hr;
2039 }
2040 
2041 /***********************************************************************
2042  *		EditStreamCut		(AVIFIL32.@)
2043  */
2044 HRESULT WINAPI EditStreamCut(PAVISTREAM pStream, LONG *plStart,
2045 			     LONG *plLength, PAVISTREAM *ppResult)
2046 {
2047   PAVIEDITSTREAM pEdit = NULL;
2048   HRESULT        hr;
2049 
2050   TRACE("(%p,%p,%p,%p)\n", pStream, plStart, plLength, ppResult);
2051 
2052   if (ppResult != NULL)
2053     *ppResult = NULL;
2054   if (pStream == NULL)
2055     return AVIERR_BADHANDLE;
2056   if (plStart == NULL || plLength == NULL)
2057     return AVIERR_BADPARAM;
2058 
2059   hr = IAVIStream_QueryInterface(pStream, &IID_IAVIEditStream,(LPVOID*)&pEdit);
2060   if (SUCCEEDED(hr) && pEdit != NULL) {
2061     hr = IAVIEditStream_Cut(pEdit, plStart, plLength, ppResult);
2062 
2063     IAVIEditStream_Release(pEdit);
2064   } else
2065     hr = AVIERR_UNSUPPORTED;
2066 
2067   return hr;
2068 }
2069 
2070 /***********************************************************************
2071  *		EditStreamPaste		(AVIFIL32.@)
2072  */
2073 HRESULT WINAPI EditStreamPaste(PAVISTREAM pDest, LONG *plStart, LONG *plLength,
2074 			       PAVISTREAM pSource, LONG lStart, LONG lEnd)
2075 {
2076   PAVIEDITSTREAM pEdit = NULL;
2077   HRESULT        hr;
2078 
2079   TRACE("(%p,%p,%p,%p,%d,%d)\n", pDest, plStart, plLength,
2080 	pSource, lStart, lEnd);
2081 
2082   if (pDest == NULL || pSource == NULL)
2083     return AVIERR_BADHANDLE;
2084   if (plStart == NULL || plLength == NULL || lStart < 0)
2085     return AVIERR_BADPARAM;
2086 
2087   hr = IAVIStream_QueryInterface(pDest, &IID_IAVIEditStream,(LPVOID*)&pEdit);
2088   if (SUCCEEDED(hr) && pEdit != NULL) {
2089     hr = IAVIEditStream_Paste(pEdit, plStart, plLength, pSource, lStart, lEnd);
2090 
2091     IAVIEditStream_Release(pEdit);
2092   } else
2093     hr = AVIERR_UNSUPPORTED;
2094 
2095   return hr;
2096 }
2097 
2098 /***********************************************************************
2099  *		EditStreamSetInfoA	(AVIFIL32.@)
2100  */
2101 HRESULT WINAPI EditStreamSetInfoA(PAVISTREAM pstream, LPAVISTREAMINFOA asi,
2102 				  LONG size)
2103 {
2104   AVISTREAMINFOW asiw;
2105 
2106   TRACE("(%p,%p,%d)\n", pstream, asi, size);
2107 
2108   if (size >= 0 && size < sizeof(AVISTREAMINFOA))
2109     return AVIERR_BADSIZE;
2110 
2111   memcpy(&asiw, asi, sizeof(asiw) - sizeof(asiw.szName));
2112   MultiByteToWideChar(CP_ACP, 0, asi->szName, -1,
2113 		      asiw.szName, sizeof(asiw.szName)/sizeof(WCHAR));
2114 
2115   return EditStreamSetInfoW(pstream, &asiw, sizeof(asiw));
2116 }
2117 
2118 /***********************************************************************
2119  *		EditStreamSetInfoW	(AVIFIL32.@)
2120  */
2121 HRESULT WINAPI EditStreamSetInfoW(PAVISTREAM pstream, LPAVISTREAMINFOW asi,
2122 				  LONG size)
2123 {
2124   PAVIEDITSTREAM pEdit = NULL;
2125   HRESULT        hr;
2126 
2127   TRACE("(%p,%p,%d)\n", pstream, asi, size);
2128 
2129   if (size >= 0 && size < sizeof(AVISTREAMINFOA))
2130     return AVIERR_BADSIZE;
2131 
2132   hr = IAVIStream_QueryInterface(pstream, &IID_IAVIEditStream,(LPVOID*)&pEdit);
2133   if (SUCCEEDED(hr) && pEdit != NULL) {
2134     hr = IAVIEditStream_SetInfo(pEdit, asi, size);
2135 
2136     IAVIEditStream_Release(pEdit);
2137   } else
2138     hr = AVIERR_UNSUPPORTED;
2139 
2140   return hr;
2141 }
2142 
2143 /***********************************************************************
2144  *		EditStreamSetNameA	(AVIFIL32.@)
2145  */
2146 HRESULT WINAPI EditStreamSetNameA(PAVISTREAM pstream, LPCSTR szName)
2147 {
2148   AVISTREAMINFOA asia;
2149   HRESULT        hres;
2150 
2151   TRACE("(%p,%s)\n", pstream, debugstr_a(szName));
2152 
2153   if (pstream == NULL)
2154     return AVIERR_BADHANDLE;
2155   if (szName == NULL)
2156     return AVIERR_BADPARAM;
2157 
2158   hres = AVIStreamInfoA(pstream, &asia, sizeof(asia));
2159   if (FAILED(hres))
2160     return hres;
2161 
2162   memset(asia.szName, 0, sizeof(asia.szName));
2163   lstrcpynA(asia.szName, szName, sizeof(asia.szName)/sizeof(asia.szName[0]));
2164 
2165   return EditStreamSetInfoA(pstream, &asia, sizeof(asia));
2166 }
2167 
2168 /***********************************************************************
2169  *		EditStreamSetNameW	(AVIFIL32.@)
2170  */
2171 HRESULT WINAPI EditStreamSetNameW(PAVISTREAM pstream, LPCWSTR szName)
2172 {
2173   AVISTREAMINFOW asiw;
2174   HRESULT        hres;
2175 
2176   TRACE("(%p,%s)\n", pstream, debugstr_w(szName));
2177 
2178   if (pstream == NULL)
2179     return AVIERR_BADHANDLE;
2180   if (szName == NULL)
2181     return AVIERR_BADPARAM;
2182 
2183   hres = IAVIStream_Info(pstream, &asiw, sizeof(asiw));
2184   if (FAILED(hres))
2185     return hres;
2186 
2187   memset(asiw.szName, 0, sizeof(asiw.szName));
2188   lstrcpynW(asiw.szName, szName, sizeof(asiw.szName)/sizeof(asiw.szName[0]));
2189 
2190   return EditStreamSetInfoW(pstream, &asiw, sizeof(asiw));
2191 }
2192 
2193 /***********************************************************************
2194  *		AVIClearClipboard	(AVIFIL32.@)
2195  */
2196 HRESULT WINAPI AVIClearClipboard(void)
2197 {
2198   TRACE("()\n");
2199 
2200   return AVIERR_UNSUPPORTED; /* OleSetClipboard(NULL); */
2201 }
2202 
2203 /***********************************************************************
2204  *		AVIGetFromClipboard	(AVIFIL32.@)
2205  */
2206 HRESULT WINAPI AVIGetFromClipboard(PAVIFILE *ppfile)
2207 {
2208   FIXME("(%p), stub!\n", ppfile);
2209 
2210   *ppfile = NULL;
2211 
2212   return AVIERR_UNSUPPORTED;
2213 }
2214 
2215 /***********************************************************************
2216  *      AVIMakeStreamFromClipboard (AVIFIL32.@)
2217  */
2218 HRESULT WINAPI AVIMakeStreamFromClipboard(UINT cfFormat, HANDLE hGlobal,
2219                                           PAVISTREAM * ppstream)
2220 {
2221   FIXME("(0x%08x,%p,%p), stub!\n", cfFormat, hGlobal, ppstream);
2222 
2223   if (ppstream == NULL)
2224     return AVIERR_BADHANDLE;
2225 
2226   return AVIERR_UNSUPPORTED;
2227 }
2228 
2229 /***********************************************************************
2230  *		AVIPutFileOnClipboard	(AVIFIL32.@)
2231  */
2232 HRESULT WINAPI AVIPutFileOnClipboard(PAVIFILE pfile)
2233 {
2234   FIXME("(%p), stub!\n", pfile);
2235 
2236   if (pfile == NULL)
2237     return AVIERR_BADHANDLE;
2238 
2239   return AVIERR_UNSUPPORTED;
2240 }
2241 
2242 HRESULT WINAPIV AVISaveA(LPCSTR szFile, CLSID * pclsidHandler, AVISAVECALLBACK lpfnCallback,
2243                         int nStreams, PAVISTREAM pavi, LPAVICOMPRESSOPTIONS lpOptions, ...)
2244 {
2245     __ms_va_list vl;
2246     int i;
2247     HRESULT ret;
2248     PAVISTREAM *streams;
2249     LPAVICOMPRESSOPTIONS *options;
2250 
2251     TRACE("(%s,%p,%p,%d,%p,%p)\n", debugstr_a(szFile), pclsidHandler, lpfnCallback,
2252           nStreams, pavi, lpOptions);
2253 
2254     if (nStreams <= 0) return AVIERR_BADPARAM;
2255 
2256     streams = HeapAlloc(GetProcessHeap(), 0, nStreams * sizeof(*streams));
2257     options = HeapAlloc(GetProcessHeap(), 0, nStreams * sizeof(*options));
2258     if (!streams || !options)
2259     {
2260         ret = AVIERR_MEMORY;
2261         goto error;
2262     }
2263 
2264     streams[0] = pavi;
2265     options[0] = lpOptions;
2266 
2267     __ms_va_start(vl, lpOptions);
2268     for (i = 1; i < nStreams; i++)
2269     {
2270         streams[i] = va_arg(vl, PAVISTREAM);
2271         options[i] = va_arg(vl, PAVICOMPRESSOPTIONS);
2272     }
2273     __ms_va_end(vl);
2274 
2275     for (i = 0; i < nStreams; i++)
2276         TRACE("Pair[%d] - Stream = %p, Options = %p\n", i, streams[i], options[i]);
2277 
2278     ret = AVISaveVA(szFile, pclsidHandler, lpfnCallback, nStreams, streams, options);
2279 error:
2280     HeapFree(GetProcessHeap(), 0, streams);
2281     HeapFree(GetProcessHeap(), 0, options);
2282     return ret;
2283 }
2284 
2285 HRESULT WINAPIV AVISaveW(LPCWSTR szFile, CLSID * pclsidHandler, AVISAVECALLBACK lpfnCallback,
2286                         int nStreams, PAVISTREAM pavi, LPAVICOMPRESSOPTIONS lpOptions, ...)
2287 {
2288     __ms_va_list vl;
2289     int i;
2290     HRESULT ret;
2291     PAVISTREAM *streams;
2292     LPAVICOMPRESSOPTIONS *options;
2293 
2294     TRACE("(%s,%p,%p,%d,%p,%p)\n", debugstr_w(szFile), pclsidHandler, lpfnCallback,
2295           nStreams, pavi, lpOptions);
2296 
2297     if (nStreams <= 0) return AVIERR_BADPARAM;
2298 
2299     streams = HeapAlloc(GetProcessHeap(), 0, nStreams * sizeof(*streams));
2300     options = HeapAlloc(GetProcessHeap(), 0, nStreams * sizeof(*options));
2301     if (!streams || !options)
2302     {
2303         ret = AVIERR_MEMORY;
2304         goto error;
2305     }
2306 
2307     streams[0] = pavi;
2308     options[0] = lpOptions;
2309 
2310     __ms_va_start(vl, lpOptions);
2311     for (i = 1; i < nStreams; i++)
2312     {
2313         streams[i] = va_arg(vl, PAVISTREAM);
2314         options[i] = va_arg(vl, PAVICOMPRESSOPTIONS);
2315     }
2316     __ms_va_end(vl);
2317 
2318     for (i = 0; i < nStreams; i++)
2319         TRACE("Pair[%d] - Stream = %p, Options = %p\n", i, streams[i], options[i]);
2320 
2321     ret = AVISaveVW(szFile, pclsidHandler, lpfnCallback, nStreams, streams, options);
2322 error:
2323     HeapFree(GetProcessHeap(), 0, streams);
2324     HeapFree(GetProcessHeap(), 0, options);
2325     return ret;
2326 }
2327