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