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