Test passing NULL source to WideCharToMultiByte.
[wine] / dlls / avifil32 / acmstream.c
1 /*
2  * Copyright 2002 Michael Günnewig
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  */
18
19 #define COM_NO_WINDOWS_H
20 #include <assert.h>
21 #include <stdarg.h>
22
23 #include "windef.h"
24 #include "winbase.h"
25 #include "wingdi.h"
26 #include "winuser.h"
27 #include "winnls.h"
28 #include "winerror.h"
29 #include "windowsx.h"
30 #include "mmsystem.h"
31 #include "vfw.h"
32 #include "msacm.h"
33
34 #include "avifile_private.h"
35
36 #include "wine/debug.h"
37
38 WINE_DEFAULT_DEBUG_CHANNEL(avifile);
39
40 /***********************************************************************/
41
42 static HRESULT WINAPI ACMStream_fnQueryInterface(IAVIStream*iface,REFIID refiid,LPVOID *obj);
43 static ULONG   WINAPI ACMStream_fnAddRef(IAVIStream*iface);
44 static ULONG   WINAPI ACMStream_fnRelease(IAVIStream* iface);
45 static HRESULT WINAPI ACMStream_fnCreate(IAVIStream*iface,LPARAM lParam1,LPARAM lParam2);
46 static HRESULT WINAPI ACMStream_fnInfo(IAVIStream*iface,AVISTREAMINFOW *psi,LONG size);
47 static LONG    WINAPI ACMStream_fnFindSample(IAVIStream*iface,LONG pos,LONG flags);
48 static HRESULT WINAPI ACMStream_fnReadFormat(IAVIStream*iface,LONG pos,LPVOID format,LONG *formatsize);
49 static HRESULT WINAPI ACMStream_fnSetFormat(IAVIStream*iface,LONG pos,LPVOID format,LONG formatsize);
50 static HRESULT WINAPI ACMStream_fnRead(IAVIStream*iface,LONG start,LONG samples,LPVOID buffer,LONG buffersize,LONG *bytesread,LONG *samplesread);
51 static HRESULT WINAPI ACMStream_fnWrite(IAVIStream*iface,LONG start,LONG samples,LPVOID buffer,LONG buffersize,DWORD flags,LONG *sampwritten,LONG *byteswritten);
52 static HRESULT WINAPI ACMStream_fnDelete(IAVIStream*iface,LONG start,LONG samples);
53 static HRESULT WINAPI ACMStream_fnReadData(IAVIStream*iface,DWORD fcc,LPVOID lp,LONG *lpread);
54 static HRESULT WINAPI ACMStream_fnWriteData(IAVIStream*iface,DWORD fcc,LPVOID lp,LONG size);
55 static HRESULT WINAPI ACMStream_fnSetInfo(IAVIStream*iface,AVISTREAMINFOW*info,LONG infolen);
56
57 static const struct IAVIStreamVtbl iacmst = {
58   ACMStream_fnQueryInterface,
59   ACMStream_fnAddRef,
60   ACMStream_fnRelease,
61   ACMStream_fnCreate,
62   ACMStream_fnInfo,
63   ACMStream_fnFindSample,
64   ACMStream_fnReadFormat,
65   ACMStream_fnSetFormat,
66   ACMStream_fnRead,
67   ACMStream_fnWrite,
68   ACMStream_fnDelete,
69   ACMStream_fnReadData,
70   ACMStream_fnWriteData,
71   ACMStream_fnSetInfo
72 };
73
74 typedef struct _IAVIStreamImpl {
75   /* IUnknown stuff */
76   const IAVIStreamVtbl *lpVtbl;
77   LONG            ref;
78
79   /* IAVIStream stuff */
80   PAVISTREAM      pStream;
81   AVISTREAMINFOW  sInfo;
82
83   HACMSTREAM      has;
84
85   LPWAVEFORMATEX  lpInFormat;
86   LONG            cbInFormat;
87
88   LPWAVEFORMATEX  lpOutFormat;
89   LONG            cbOutFormat;
90
91   ACMSTREAMHEADER acmStreamHdr;
92 } IAVIStreamImpl;
93
94 /***********************************************************************/
95
96 #define CONVERT_STREAM_to_THIS(a) { \
97            acmStreamSize(This->has,(a)*This->lpInFormat->nBlockAlign,\
98                          &(a), ACM_STREAMSIZEF_SOURCE); \
99            (a) /= This->lpOutFormat->nBlockAlign; }
100 #define CONVERT_THIS_to_STREAM(a) { \
101            acmStreamSize(This->has,(a)*This->lpOutFormat->nBlockAlign,\
102                          &(a), ACM_STREAMSIZEF_DESTINATION); \
103            (a) /= This->lpInFormat->nBlockAlign; }
104
105 static HRESULT AVIFILE_OpenCompressor(IAVIStreamImpl *This);
106
107 HRESULT AVIFILE_CreateACMStream(REFIID riid, LPVOID *ppv)
108 {
109   IAVIStreamImpl *pstream;
110   HRESULT         hr;
111
112   assert(riid != NULL && ppv != NULL);
113
114   *ppv = NULL;
115
116   pstream = (IAVIStreamImpl*)LocalAlloc(LPTR, sizeof(IAVIStreamImpl));
117   if (pstream == NULL)
118     return AVIERR_MEMORY;
119
120   pstream->lpVtbl = &iacmst;
121
122   hr = IAVIStream_QueryInterface((IAVIStream*)pstream, riid, ppv);
123   if (FAILED(hr))
124     LocalFree((HLOCAL)pstream);
125
126   return hr;
127 }
128
129 static HRESULT WINAPI ACMStream_fnQueryInterface(IAVIStream *iface,
130                                                   REFIID refiid, LPVOID *obj)
131 {
132   IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
133
134   TRACE("(%p,%s,%p)\n", iface, debugstr_guid(refiid), obj);
135
136   if (IsEqualGUID(&IID_IUnknown, refiid) ||
137       IsEqualGUID(&IID_IAVIStream, refiid)) {
138     *obj = This;
139     IAVIStream_AddRef(iface);
140
141     return S_OK;
142   }
143
144   return OLE_E_ENUM_NOMORE;
145 }
146
147 static ULONG WINAPI ACMStream_fnAddRef(IAVIStream *iface)
148 {
149   IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
150   ULONG ref = InterlockedIncrement(&This->ref);
151
152   TRACE("(%p) -> %ld\n", iface, ref);
153
154   /* also add reference to the nested stream */
155   if (This->pStream != NULL)
156     IAVIStream_AddRef(This->pStream);
157
158   return ref;
159 }
160
161 static ULONG WINAPI ACMStream_fnRelease(IAVIStream* iface)
162 {
163   IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
164   ULONG ref = InterlockedDecrement(&This->ref);
165
166   TRACE("(%p) -> %ld\n", iface, ref);
167
168   if (ref == 0) {
169     /* destruct */
170     if (This->has != NULL) {
171       if (This->acmStreamHdr.fdwStatus & ACMSTREAMHEADER_STATUSF_PREPARED)
172         acmStreamUnprepareHeader(This->has, &This->acmStreamHdr, 0);
173       acmStreamClose(This->has, 0);
174       This->has = NULL;
175     }
176     if (This->acmStreamHdr.pbSrc != NULL) {
177       GlobalFreePtr(This->acmStreamHdr.pbSrc);
178       This->acmStreamHdr.pbSrc = NULL;
179     }
180     if (This->acmStreamHdr.pbDst != NULL) {
181       GlobalFreePtr(This->acmStreamHdr.pbDst);
182       This->acmStreamHdr.pbDst = NULL;
183     }
184     if (This->lpInFormat != NULL) {
185       GlobalFreePtr(This->lpInFormat);
186       This->lpInFormat = NULL;
187       This->cbInFormat = 0;
188     }
189     if (This->lpOutFormat != NULL) {
190       GlobalFreePtr(This->lpOutFormat);
191       This->lpOutFormat = NULL;
192       This->cbOutFormat = 0;
193     }
194     if (This->pStream != NULL) {
195       IAVIStream_Release(This->pStream);
196       This->pStream = NULL;
197     }
198     LocalFree((HLOCAL)This);
199
200     return 0;
201   }
202
203   /* also release reference to the nested stream */
204   if (This->pStream != NULL)
205     IAVIStream_Release(This->pStream);
206
207   return ref;
208 }
209
210 /* lParam1: PAVISTREAM
211  * lParam2: LPAVICOMPRESSOPTIONS -- even if doc's say LPWAVEFORMAT
212  */
213 static HRESULT WINAPI ACMStream_fnCreate(IAVIStream *iface, LPARAM lParam1,
214                                           LPARAM lParam2)
215 {
216   IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
217
218   TRACE("(%p,0x%08lX,0x%08lX)\n", iface, lParam1, lParam2);
219
220   /* check for swapped parameters */
221   if ((LPVOID)lParam1 != NULL &&
222       ((LPAVICOMPRESSOPTIONS)lParam1)->fccType == streamtypeAUDIO) {
223     register LPARAM tmp = lParam1;
224
225     lParam1 = lParam2;
226     lParam2 = tmp;
227   }
228
229   if ((LPVOID)lParam1 == NULL)
230     return AVIERR_BADPARAM;
231
232   IAVIStream_Info((PAVISTREAM)lParam1, &This->sInfo, sizeof(This->sInfo));
233   if (This->sInfo.fccType != streamtypeAUDIO)
234     return AVIERR_ERROR; /* error in registry or AVIMakeCompressedStream */
235
236   This->sInfo.fccHandler = 0; /* be paranoid */
237
238   /* FIXME: check ACM version? Which version does we need? */
239
240   if ((LPVOID)lParam2 != NULL) {
241     /* We only need the format from the compress-options */
242     if (((LPAVICOMPRESSOPTIONS)lParam2)->fccType == streamtypeAUDIO)
243       lParam2 = (LPARAM)((LPAVICOMPRESSOPTIONS)lParam2)->lpFormat;
244
245     if (((LPWAVEFORMATEX)lParam2)->wFormatTag != WAVE_FORMAT_PCM)
246       This->cbOutFormat = sizeof(WAVEFORMATEX) + ((LPWAVEFORMATEX)lParam2)->cbSize;
247     else
248       This->cbOutFormat = sizeof(PCMWAVEFORMAT);
249
250     This->lpOutFormat = (LPWAVEFORMATEX)GlobalAllocPtr(GHND, This->cbOutFormat);
251     if (This->lpOutFormat == NULL)
252       return AVIERR_MEMORY;
253
254     memcpy(This->lpOutFormat, (LPVOID)lParam2, This->cbOutFormat);
255   } else {
256     This->lpOutFormat = NULL;
257     This->cbOutFormat = 0;
258   }
259
260   This->pStream = (PAVISTREAM)lParam1;
261   IAVIStream_AddRef(This->pStream);
262
263   return AVIERR_OK;
264 }
265
266 static HRESULT WINAPI ACMStream_fnInfo(IAVIStream *iface,LPAVISTREAMINFOW psi,
267                                         LONG size)
268 {
269   IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
270
271   TRACE("(%p,%p,%ld)\n", iface, psi, size);
272
273   if (psi == NULL)
274     return AVIERR_BADPARAM;
275   if (size < 0)
276     return AVIERR_BADSIZE;
277
278   /* Need codec to correct some values in structure */
279   if (This->has == NULL) {
280     HRESULT hr = AVIFILE_OpenCompressor(This);
281
282     if (FAILED(hr))
283       return hr;
284   }
285
286   memcpy(psi, &This->sInfo, min(size, (LONG)sizeof(This->sInfo)));
287
288   if (size < (LONG)sizeof(This->sInfo))
289     return AVIERR_BUFFERTOOSMALL;
290   return AVIERR_OK;
291 }
292
293 static LONG WINAPI ACMStream_fnFindSample(IAVIStream *iface, LONG pos,
294                                            LONG flags)
295 {
296   IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
297
298   TRACE("(%p,%ld,0x%08lX)\n",iface,pos,flags);
299
300   if (flags & FIND_FROM_START) {
301     pos = This->sInfo.dwStart;
302     flags &= ~(FIND_FROM_START|FIND_PREV);
303     flags |= FIND_NEXT;
304   }
305
306   /* convert pos from our 'space' to This->pStream's one */
307   CONVERT_THIS_to_STREAM(pos);
308
309   /* ask stream */
310   pos = IAVIStream_FindSample(This->pStream, pos, flags);
311
312   if (pos != -1) {
313     /* convert pos back to our 'space' if it's no size or physical pos */
314     if ((flags & FIND_RET) == 0)
315       CONVERT_STREAM_to_THIS(pos);
316   }
317
318   return pos;
319 }
320
321 static HRESULT WINAPI ACMStream_fnReadFormat(IAVIStream *iface, LONG pos,
322                                               LPVOID format, LONG *formatsize)
323 {
324   IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
325
326   TRACE("(%p,%ld,%p,%p)\n", iface, pos, format, formatsize);
327
328   if (formatsize == NULL)
329     return AVIERR_BADPARAM;
330
331   if (This->has == NULL) {
332     HRESULT hr = AVIFILE_OpenCompressor(This);
333
334     if (FAILED(hr))
335       return hr;
336   }
337
338   /* only interested in needed buffersize? */
339   if (format == NULL || *formatsize <= 0) {
340     *formatsize = This->cbOutFormat;
341
342     return AVIERR_OK;
343   }
344
345   /* copy initial format (only as much as will fit) */
346   memcpy(format, This->lpOutFormat, min(*formatsize, This->cbOutFormat));
347   if (*formatsize < This->cbOutFormat) {
348     *formatsize = This->cbOutFormat;
349     return AVIERR_BUFFERTOOSMALL;
350   }
351
352   *formatsize = This->cbOutFormat;
353   return AVIERR_OK;
354 }
355
356 static HRESULT WINAPI ACMStream_fnSetFormat(IAVIStream *iface, LONG pos,
357                                              LPVOID format, LONG formatsize)
358 {
359   IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
360
361   HRESULT hr;
362
363   TRACE("(%p,%ld,%p,%ld)\n", iface, pos, format, formatsize);
364
365   /* check parameters */
366   if (format == NULL || formatsize <= 0)
367     return AVIERR_BADPARAM;
368
369   /* Input format already known?
370    * Changing is unsupported, but be quiet if it's the same */
371   if (This->lpInFormat != NULL) {
372     if (This->cbInFormat != formatsize ||
373         memcmp(format, This->lpInFormat, formatsize) != 0)
374       return AVIERR_UNSUPPORTED;
375
376     return AVIERR_OK;
377   }
378
379   /* Does the nested stream support writing? */
380   if ((This->sInfo.dwCaps & AVIFILECAPS_CANWRITE) == 0)
381     return AVIERR_READONLY;
382
383   This->lpInFormat = (LPWAVEFORMATEX)GlobalAllocPtr(GMEM_MOVEABLE, formatsize);
384   if (This->lpInFormat == NULL)
385     return AVIERR_MEMORY;
386   This->cbInFormat = formatsize;
387   memcpy(This->lpInFormat, format, formatsize);
388
389   /* initialize formats and get compressor */
390   hr = AVIFILE_OpenCompressor(This);
391   if (FAILED(hr))
392     return hr;
393
394   CONVERT_THIS_to_STREAM(pos);
395
396   /* tell the nested stream the new format */
397   return IAVIStream_SetFormat(This->pStream, pos, This->lpOutFormat,
398                               This->cbOutFormat);
399 }
400
401 static HRESULT WINAPI ACMStream_fnRead(IAVIStream *iface, LONG start,
402                                         LONG samples, LPVOID buffer,
403                                         LONG buffersize, LPLONG bytesread,
404                                         LPLONG samplesread)
405 {
406   IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
407
408   HRESULT hr;
409   DWORD   size;
410
411   TRACE("(%p,%ld,%ld,%p,%ld,%p,%p)\n", iface, start, samples, buffer,
412         buffersize, bytesread, samplesread);
413
414   /* clear return parameters if given */
415   if (bytesread != NULL)
416     *bytesread = 0;
417   if (samplesread != NULL)
418     *samplesread = 0;
419
420   /* Do we have our compressor? */
421   if (This->has == NULL) {
422     hr = AVIFILE_OpenCompressor(This);
423
424     if (FAILED(hr))
425       return hr;
426   }
427
428   /* only need to pass through? */
429   if (This->cbInFormat == This->cbOutFormat &&
430       memcmp(This->lpInFormat, This->lpOutFormat, This->cbInFormat) == 0) {
431     return IAVIStream_Read(This->pStream, start, samples, buffer, buffersize,
432                            bytesread, samplesread);
433   }
434
435   /* read as much as fit? */
436   if (samples == -1)
437     samples = buffersize / This->lpOutFormat->nBlockAlign;
438   /* limit to buffersize */
439   if (samples * This->lpOutFormat->nBlockAlign > buffersize)
440     samples = buffersize / This->lpOutFormat->nBlockAlign;
441
442   /* only return needed size? */
443   if (buffer == NULL || buffersize <= 0 || samples == 0) {
444     if (bytesread == NULL && samplesread == NULL)
445       return AVIERR_BADPARAM;
446
447     if (bytesread != NULL)
448       *bytesread = samples * This->lpOutFormat->nBlockAlign;
449     if (samplesread != NULL)
450       *samplesread = samples;
451
452     return AVIERR_OK;
453   }
454
455   /* map our positions to pStream positions */
456   CONVERT_THIS_to_STREAM(start);
457
458   /* our needed internal buffersize */
459   size = samples * This->lpInFormat->nBlockAlign;
460
461   /* Need to free destination buffer used for writing? */
462   if (This->acmStreamHdr.pbDst != NULL) {
463     GlobalFreePtr(This->acmStreamHdr.pbDst);
464     This->acmStreamHdr.pbDst     = NULL;
465     This->acmStreamHdr.dwDstUser = 0;
466   }
467
468   /* need bigger source buffer? */
469   if (This->acmStreamHdr.pbSrc == NULL ||
470       This->acmStreamHdr.dwSrcUser < size) {
471     if (This->acmStreamHdr.pbSrc == NULL)
472       This->acmStreamHdr.pbSrc = GlobalAllocPtr(GMEM_MOVEABLE, size);
473     else
474       This->acmStreamHdr.pbSrc = GlobalReAllocPtr(This->acmStreamHdr.pbSrc,
475                                                   size, GMEM_MOVEABLE);
476     if (This->acmStreamHdr.pbSrc == NULL)
477       return AVIERR_MEMORY;
478     This->acmStreamHdr.dwSrcUser = size;
479   }
480
481   This->acmStreamHdr.cbStruct = sizeof(This->acmStreamHdr);
482   This->acmStreamHdr.cbSrcLengthUsed = 0;
483   This->acmStreamHdr.cbDstLengthUsed = 0;
484   This->acmStreamHdr.cbSrcLength     = size;
485
486   /* read source data */
487   hr = IAVIStream_Read(This->pStream, start, -1, This->acmStreamHdr.pbSrc,
488                        This->acmStreamHdr.cbSrcLength,
489                        &This->acmStreamHdr.cbSrcLength, NULL);
490   if (FAILED(hr) || This->acmStreamHdr.cbSrcLength == 0)
491     return hr;
492
493   /* need to prepare stream? */
494   This->acmStreamHdr.pbDst       = buffer;
495   This->acmStreamHdr.cbDstLength = buffersize;
496   if ((This->acmStreamHdr.fdwStatus & ACMSTREAMHEADER_STATUSF_PREPARED) == 0) {
497     if (acmStreamPrepareHeader(This->has, &This->acmStreamHdr, 0) != S_OK) {
498       This->acmStreamHdr.pbDst       = NULL;
499       This->acmStreamHdr.cbDstLength = 0;
500       return AVIERR_COMPRESSOR;
501     }
502   }
503
504   /* now do the conversion */
505   /* FIXME: use ACM_CONVERTF_* flags */
506   if (acmStreamConvert(This->has, &This->acmStreamHdr, 0) != S_OK)
507     hr = AVIERR_COMPRESSOR;
508
509   This->acmStreamHdr.pbDst       = NULL;
510   This->acmStreamHdr.cbDstLength = 0;
511
512   /* fill out return parameters if given */
513   if (bytesread != NULL)
514     *bytesread = This->acmStreamHdr.cbDstLengthUsed;
515   if (samplesread != NULL)
516     *samplesread =
517       This->acmStreamHdr.cbDstLengthUsed / This->lpOutFormat->nBlockAlign;
518
519   return hr;
520 }
521
522 static HRESULT WINAPI ACMStream_fnWrite(IAVIStream *iface, LONG start,
523                                          LONG samples, LPVOID buffer,
524                                          LONG buffersize, DWORD flags,
525                                          LPLONG sampwritten,
526                                          LPLONG byteswritten)
527 {
528   IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
529
530   HRESULT hr;
531   ULONG   size;
532
533   TRACE("(%p,%ld,%ld,%p,%ld,0x%08lX,%p,%p)\n", iface, start, samples,
534         buffer, buffersize, flags, sampwritten, byteswritten);
535
536   /* clear return parameters if given */
537   if (sampwritten != NULL)
538     *sampwritten = 0;
539   if (byteswritten != NULL)
540     *byteswritten = 0;
541
542   /* check parameters */
543   if (buffer == NULL && (buffersize > 0 || samples > 0))
544     return AVIERR_BADPARAM;
545
546   /* Have we write capability? */
547   if ((This->sInfo.dwCaps & AVIFILECAPS_CANWRITE) == 0)
548     return AVIERR_READONLY;
549
550   /* also need a compressor */
551   if (This->has == NULL)
552     return AVIERR_NOCOMPRESSOR;
553
554   /* map our sizes to pStream sizes */
555   size = buffersize;
556   CONVERT_THIS_to_STREAM(size);
557   CONVERT_THIS_to_STREAM(start);
558
559   /* no bytes to write? -- short circuit */
560   if (size == 0) {
561     return IAVIStream_Write(This->pStream, -1, samples, buffer, size,
562                             flags, sampwritten, byteswritten);
563   }
564
565   /* Need to free source buffer used for reading? */
566   if (This->acmStreamHdr.pbSrc != NULL) {
567     GlobalFreePtr(This->acmStreamHdr.pbSrc);
568     This->acmStreamHdr.pbSrc     = NULL;
569     This->acmStreamHdr.dwSrcUser = 0;
570   }
571
572   /* Need bigger destination buffer? */
573   if (This->acmStreamHdr.pbDst == NULL ||
574       This->acmStreamHdr.dwDstUser < size) {
575     if (This->acmStreamHdr.pbDst == NULL)
576       This->acmStreamHdr.pbDst = GlobalAllocPtr(GMEM_MOVEABLE, size);
577     else
578       This->acmStreamHdr.pbDst = GlobalReAllocPtr(This->acmStreamHdr.pbDst,
579                                                   size, GMEM_MOVEABLE);
580     if (This->acmStreamHdr.pbDst == NULL)
581       return AVIERR_MEMORY;
582     This->acmStreamHdr.dwDstUser = size;
583   }
584   This->acmStreamHdr.cbStruct        = sizeof(This->acmStreamHdr);
585   This->acmStreamHdr.cbSrcLengthUsed = 0;
586   This->acmStreamHdr.cbDstLengthUsed = 0;
587   This->acmStreamHdr.cbDstLength     = This->acmStreamHdr.dwDstUser;
588
589   /* need to prepare stream? */
590   This->acmStreamHdr.pbSrc       = buffer;
591   This->acmStreamHdr.cbSrcLength = buffersize;
592   if ((This->acmStreamHdr.fdwStatus & ACMSTREAMHEADER_STATUSF_PREPARED) == 0) {
593     if (acmStreamPrepareHeader(This->has, &This->acmStreamHdr, 0) != S_OK) {
594       This->acmStreamHdr.pbSrc       = NULL;
595       This->acmStreamHdr.cbSrcLength = 0;
596       return AVIERR_COMPRESSOR;
597     }
598   }
599
600   /* now do the conversion */
601   /* FIXME: use ACM_CONVERTF_* flags */
602   if (acmStreamConvert(This->has, &This->acmStreamHdr, 0) != S_OK)
603     hr = AVIERR_COMPRESSOR;
604   else
605     hr = AVIERR_OK;
606
607   This->acmStreamHdr.pbSrc       = NULL;
608   This->acmStreamHdr.cbSrcLength = 0;
609
610   if (FAILED(hr))
611     return hr;
612
613   return IAVIStream_Write(This->pStream,-1,This->acmStreamHdr.cbDstLengthUsed /
614                           This->lpOutFormat->nBlockAlign,This->acmStreamHdr.pbDst,
615                           This->acmStreamHdr.cbDstLengthUsed,flags,sampwritten,
616                           byteswritten);
617 }
618
619 static HRESULT WINAPI ACMStream_fnDelete(IAVIStream *iface, LONG start,
620                                           LONG samples)
621 {
622   IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
623
624   TRACE("(%p,%ld,%ld)\n", iface, start, samples);
625
626   /* check parameters */
627   if (start < 0 || samples < 0)
628     return AVIERR_BADPARAM;
629
630   /* Delete before start of stream? */
631   if ((DWORD)(start + samples) < This->sInfo.dwStart)
632     return AVIERR_OK;
633
634   /* Delete after end of stream? */
635   if ((DWORD)start > This->sInfo.dwLength)
636     return AVIERR_OK;
637
638   /* For the rest we need write capability */
639   if ((This->sInfo.dwCaps & AVIFILECAPS_CANWRITE) == 0)
640     return AVIERR_READONLY;
641
642   /* A compressor is also necessary */
643   if (This->has == NULL)
644     return AVIERR_NOCOMPRESSOR;
645
646   /* map our positions to pStream positions */
647   CONVERT_THIS_to_STREAM(start);
648   CONVERT_THIS_to_STREAM(samples);
649
650   return IAVIStream_Delete(This->pStream, start, samples);
651 }
652
653 static HRESULT WINAPI ACMStream_fnReadData(IAVIStream *iface, DWORD fcc,
654                                             LPVOID lp, LPLONG lpread)
655 {
656   IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
657
658   TRACE("(%p,0x%08lX,%p,%p)\n", iface, fcc, lp, lpread);
659
660   assert(This->pStream != NULL);
661
662   return IAVIStream_ReadData(This->pStream, fcc, lp, lpread);
663 }
664
665 static HRESULT WINAPI ACMStream_fnWriteData(IAVIStream *iface, DWORD fcc,
666                                              LPVOID lp, LONG size)
667 {
668   IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
669
670   TRACE("(%p,0x%08lx,%p,%ld)\n", iface, fcc, lp, size);
671
672   assert(This->pStream != NULL);
673
674   return IAVIStream_WriteData(This->pStream, fcc, lp, size);
675 }
676
677 static HRESULT WINAPI ACMStream_fnSetInfo(IAVIStream *iface,
678                                            LPAVISTREAMINFOW info, LONG infolen)
679 {
680   FIXME("(%p,%p,%ld): stub\n", iface, info, infolen);
681
682   return E_FAIL;
683 }
684
685 /***********************************************************************/
686
687 static HRESULT AVIFILE_OpenCompressor(IAVIStreamImpl *This)
688 {
689   HRESULT hr;
690
691   /* pre-conditions */
692   assert(This != NULL);
693   assert(This->pStream != NULL);
694
695   if (This->has != NULL)
696     return AVIERR_OK;
697
698   if (This->lpInFormat == NULL) {
699     /* decode or encode the data from pStream */
700     hr = AVIStreamFormatSize(This->pStream, This->sInfo.dwStart, &This->cbInFormat);
701     if (FAILED(hr))
702       return hr;
703     This->lpInFormat = (LPWAVEFORMATEX)GlobalAllocPtr(GMEM_MOVEABLE, This->cbInFormat);
704     if (This->lpInFormat == NULL)
705       return AVIERR_MEMORY;
706
707     hr = IAVIStream_ReadFormat(This->pStream, This->sInfo.dwStart,
708                                This->lpInFormat, &This->cbInFormat);
709     if (FAILED(hr))
710       return hr;
711
712     if (This->lpOutFormat == NULL) {
713       /* we must decode to default format */
714       This->cbOutFormat = sizeof(PCMWAVEFORMAT);
715       This->lpOutFormat = (LPWAVEFORMATEX)GlobalAllocPtr(GHND, This->cbOutFormat);
716       if (This->lpOutFormat == NULL)
717         return AVIERR_MEMORY;
718
719       This->lpOutFormat->wFormatTag = WAVE_FORMAT_PCM;
720       if (acmFormatSuggest(NULL, This->lpInFormat, This->lpOutFormat,
721                            This->cbOutFormat, ACM_FORMATSUGGESTF_WFORMATTAG) != S_OK)
722         return AVIERR_NOCOMPRESSOR;
723     }
724   } else if (This->lpOutFormat == NULL)
725     return AVIERR_ERROR; /* To what should I encode? */
726
727   if (acmStreamOpen(&This->has, NULL, This->lpInFormat, This->lpOutFormat,
728                     NULL, 0, 0, ACM_STREAMOPENF_NONREALTIME) != S_OK)
729     return AVIERR_NOCOMPRESSOR;
730
731   /* update AVISTREAMINFO structure */
732   This->sInfo.dwSampleSize = This->lpOutFormat->nBlockAlign;
733   This->sInfo.dwScale      = This->lpOutFormat->nBlockAlign;
734   This->sInfo.dwRate       = This->lpOutFormat->nAvgBytesPerSec;
735   This->sInfo.dwQuality    = (DWORD)ICQUALITY_DEFAULT;
736   SetRectEmpty(&This->sInfo.rcFrame);
737
738   /* convert positions ansd sizes to output format */
739   CONVERT_STREAM_to_THIS(This->sInfo.dwStart);
740   CONVERT_STREAM_to_THIS(This->sInfo.dwLength);
741   CONVERT_STREAM_to_THIS(This->sInfo.dwSuggestedBufferSize);
742
743   return AVIERR_OK;
744 }