Implement A->W call for GetNamedSecurityInfo.
[wine] / dlls / avifil32 / icmstream.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 #define MAX_FRAMESIZE       (16 * 1024 * 1024)
41 #define MAX_FRAMESIZE_DIFF  512
42
43 /***********************************************************************/
44
45 static HRESULT WINAPI ICMStream_fnQueryInterface(IAVIStream*iface,REFIID refiid,LPVOID *obj);
46 static ULONG   WINAPI ICMStream_fnAddRef(IAVIStream*iface);
47 static ULONG   WINAPI ICMStream_fnRelease(IAVIStream* iface);
48 static HRESULT WINAPI ICMStream_fnCreate(IAVIStream*iface,LPARAM lParam1,LPARAM lParam2);
49 static HRESULT WINAPI ICMStream_fnInfo(IAVIStream*iface,AVISTREAMINFOW *psi,LONG size);
50 static LONG    WINAPI ICMStream_fnFindSample(IAVIStream*iface,LONG pos,LONG flags);
51 static HRESULT WINAPI ICMStream_fnReadFormat(IAVIStream*iface,LONG pos,LPVOID format,LONG *formatsize);
52 static HRESULT WINAPI ICMStream_fnSetFormat(IAVIStream*iface,LONG pos,LPVOID format,LONG formatsize);
53 static HRESULT WINAPI ICMStream_fnRead(IAVIStream*iface,LONG start,LONG samples,LPVOID buffer,LONG buffersize,LONG *bytesread,LONG *samplesread);
54 static HRESULT WINAPI ICMStream_fnWrite(IAVIStream*iface,LONG start,LONG samples,LPVOID buffer,LONG buffersize,DWORD flags,LONG *sampwritten,LONG *byteswritten);
55 static HRESULT WINAPI ICMStream_fnDelete(IAVIStream*iface,LONG start,LONG samples);
56 static HRESULT WINAPI ICMStream_fnReadData(IAVIStream*iface,DWORD fcc,LPVOID lp,LONG *lpread);
57 static HRESULT WINAPI ICMStream_fnWriteData(IAVIStream*iface,DWORD fcc,LPVOID lp,LONG size);
58 static HRESULT WINAPI ICMStream_fnSetInfo(IAVIStream*iface,AVISTREAMINFOW*info,LONG infolen);
59
60 struct ICOM_VTABLE(IAVIStream) iicmst = {
61   ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
62   ICMStream_fnQueryInterface,
63   ICMStream_fnAddRef,
64   ICMStream_fnRelease,
65   ICMStream_fnCreate,
66   ICMStream_fnInfo,
67   ICMStream_fnFindSample,
68   ICMStream_fnReadFormat,
69   ICMStream_fnSetFormat,
70   ICMStream_fnRead,
71   ICMStream_fnWrite,
72   ICMStream_fnDelete,
73   ICMStream_fnReadData,
74   ICMStream_fnWriteData,
75   ICMStream_fnSetInfo
76 };
77
78 typedef struct _IAVIStreamImpl {
79   /* IUnknown stuff */
80   ICOM_VFIELD(IAVIStream);
81   DWORD              ref;
82
83   /* IAVIStream stuff */
84   PAVISTREAM         pStream;
85   AVISTREAMINFOW     sInfo;
86
87   PGETFRAME          pg;
88   HIC                hic;
89   DWORD              dwICMFlags;
90
91   LONG               lCurrent;
92   LONG               lLastKey;
93   LONG               lKeyFrameEvery;
94   DWORD              dwLastQuality;
95   DWORD              dwBytesPerFrame;
96   DWORD              dwUnusedBytes;
97
98   LPBITMAPINFOHEADER lpbiCur;  /* current frame */
99   LPVOID             lpCur;
100   LPBITMAPINFOHEADER lpbiPrev; /* previous frame */
101   LPVOID             lpPrev;
102
103   LPBITMAPINFOHEADER lpbiOutput; /* output format of codec */
104   LONG               cbOutput;
105   LPBITMAPINFOHEADER lpbiInput;  /* input format for codec */
106   LONG               cbInput;
107 } IAVIStreamImpl;
108
109 /***********************************************************************/
110
111 static HRESULT AVIFILE_EncodeFrame(IAVIStreamImpl *This,
112                                    LPBITMAPINFOHEADER lpbi, LPVOID lpBits);
113 static HRESULT AVIFILE_OpenGetFrame(IAVIStreamImpl *This);
114
115 inline void AVIFILE_Reset(IAVIStreamImpl *This)
116 {
117   This->lCurrent      = -1;
118   This->lLastKey      = 0;
119   This->dwLastQuality = ICQUALITY_HIGH;
120   This->dwUnusedBytes = 0;
121 }
122
123 HRESULT AVIFILE_CreateICMStream(REFIID riid, LPVOID *ppv)
124 {
125   IAVIStreamImpl *pstream;
126   HRESULT         hr;
127
128   assert(riid != NULL && ppv != NULL);
129
130   *ppv = NULL;
131
132   pstream = (IAVIStreamImpl*)LocalAlloc(LPTR, sizeof(IAVIStreamImpl));
133   if (pstream == NULL)
134     return AVIERR_MEMORY;
135
136   pstream->lpVtbl  = &iicmst;
137   AVIFILE_Reset(pstream);
138
139   hr = IUnknown_QueryInterface((IUnknown*)pstream, riid, ppv);
140   if (FAILED(hr))
141     LocalFree((HLOCAL)pstream);
142
143   return hr;
144 }
145
146 static HRESULT WINAPI ICMStream_fnQueryInterface(IAVIStream *iface,
147                                                   REFIID refiid, LPVOID *obj)
148 {
149   ICOM_THIS(IAVIStreamImpl,iface);
150
151   TRACE("(%p,%s,%p)\n", iface, debugstr_guid(refiid), obj);
152
153   if (IsEqualGUID(&IID_IUnknown, refiid) ||
154       IsEqualGUID(&IID_IAVIStream, refiid)) {
155     *obj = This;
156     IAVIStream_AddRef(iface);
157
158     return S_OK;
159   }
160
161   return OLE_E_ENUM_NOMORE;
162 }
163
164 static ULONG WINAPI ICMStream_fnAddRef(IAVIStream *iface)
165 {
166   ICOM_THIS(IAVIStreamImpl,iface);
167
168   TRACE("(%p) -> %ld\n", iface, This->ref + 1);
169
170   /* also add reference to the nested stream */
171   if (This->pStream != NULL)
172     IAVIStream_AddRef(This->pStream);
173
174   return ++(This->ref);
175 }
176
177 static ULONG WINAPI ICMStream_fnRelease(IAVIStream* iface)
178 {
179   ICOM_THIS(IAVIStreamImpl,iface);
180
181   TRACE("(%p) -> %ld\n", iface, This->ref - 1);
182
183   if (This->ref == 0) {
184     /* destruct */
185     if (This->pg != NULL) {
186       AVIStreamGetFrameClose(This->pg);
187       This->pg = NULL;
188     }
189     if (This->pStream != NULL) {
190       IAVIStream_Release(This->pStream);
191       This->pStream = NULL;
192     }
193     if (This->hic != NULL) {
194       if (This->lpbiPrev != NULL) {
195         ICDecompressEnd(This->hic);
196         GlobalFreePtr(This->lpbiPrev);
197         This->lpbiPrev = NULL;
198         This->lpPrev   = NULL;
199       }
200       ICCompressEnd(This->hic);
201       This->hic = NULL;
202     }
203     if (This->lpbiCur != NULL) {
204       GlobalFreePtr(This->lpbiCur);
205       This->lpbiCur = NULL;
206       This->lpCur   = NULL;
207     }
208     if (This->lpbiOutput != NULL) {
209       GlobalFreePtr(This->lpbiOutput);
210       This->lpbiOutput = NULL;
211       This->cbOutput   = 0;
212     }
213     if (This->lpbiInput != NULL) {
214       GlobalFreePtr(This->lpbiInput);
215       This->lpbiInput = NULL;
216       This->cbInput   = 0;
217     }
218
219     LocalFree((HLOCAL)This);
220
221     return 0;
222   }
223
224   /* also release reference to the nested stream */
225   if (This->pStream != NULL)
226     IAVIStream_Release(This->pStream);
227
228   return --This->ref;
229 }
230
231 /* lParam1: PAVISTREAM
232  * lParam2: LPAVICOMPRESSOPTIONS
233  */
234 static HRESULT WINAPI ICMStream_fnCreate(IAVIStream *iface, LPARAM lParam1,
235                                           LPARAM lParam2)
236 {
237   ICOM_THIS(IAVIStreamImpl,iface);
238
239   ICINFO               icinfo;
240   ICCOMPRESSFRAMES     icFrames;
241   LPAVICOMPRESSOPTIONS pco = (LPAVICOMPRESSOPTIONS)lParam2;
242
243   TRACE("(%p,0x%08lX,0x%08lX)\n", iface, lParam1, lParam2);
244
245   /* check parameter */
246   if ((LPVOID)lParam1 == NULL)
247     return AVIERR_BADPARAM;
248
249   /* get infos from stream */
250   IAVIStream_Info((PAVISTREAM)lParam1, &This->sInfo, sizeof(This->sInfo));
251   if (This->sInfo.fccType != streamtypeVIDEO)
252     return AVIERR_ERROR; /* error in registry or AVIMakeCompressedStream */
253
254   /* add reference to the stream */
255   This->pStream = (PAVISTREAM)lParam1;
256   IAVIStream_AddRef(This->pStream);
257
258   AVIFILE_Reset(This);
259
260   if (pco != NULL && pco->fccHandler != comptypeDIB) {
261     /* we should compress */
262     This->sInfo.fccHandler = pco->fccHandler;
263
264     This->hic = ICOpen(ICTYPE_VIDEO, pco->fccHandler, ICMODE_COMPRESS);
265     if (This->hic == NULL)
266       return AVIERR_NOCOMPRESSOR;
267
268     /* restore saved state of codec */
269     if (pco->cbParms > 0 && pco->lpParms != NULL) {
270       ICSetState(This->hic, pco->lpParms, pco->cbParms);
271     }
272
273     /* set quality -- resolve default quality */
274     This->sInfo.dwQuality = pco->dwQuality;
275     if (pco->dwQuality == ICQUALITY_DEFAULT)
276       This->sInfo.dwQuality = ICGetDefaultQuality(This->hic);
277
278     /* get capabilities of codec */
279     ICGetInfo(This->hic, &icinfo, sizeof(icinfo));
280     This->dwICMFlags = icinfo.dwFlags;
281
282     /* use keyframes? */
283     if ((pco->dwFlags & AVICOMPRESSF_KEYFRAMES) &&
284         (icinfo.dwFlags & (VIDCF_TEMPORAL|VIDCF_FASTTEMPORALC))) {
285       This->lKeyFrameEvery = pco->dwKeyFrameEvery;
286     } else
287       This->lKeyFrameEvery = 1;
288
289     /* use datarate? */
290     if ((pco->dwFlags & AVICOMPRESSF_DATARATE)) {
291       /* Do we have a chance to reduce size to desired one? */
292       if ((icinfo.dwFlags & (VIDCF_CRUNCH|VIDCF_QUALITY)) == 0)
293         return AVIERR_NOCOMPRESSOR;
294
295       assert(This->sInfo.dwRate != 0);
296
297       This->dwBytesPerFrame = MulDiv(pco->dwBytesPerSecond,
298                                      This->sInfo.dwScale, This->sInfo.dwRate);
299     } else {
300       pco->dwBytesPerSecond = 0;
301       This->dwBytesPerFrame = 0;
302     }
303
304     if (icinfo.dwFlags & VIDCF_COMPRESSFRAMES) {
305       memset(&icFrames, 0, sizeof(icFrames));
306       icFrames.lpbiOutput  = This->lpbiOutput;
307       icFrames.lpbiInput   = This->lpbiInput;
308       icFrames.lFrameCount = This->sInfo.dwLength;
309       icFrames.lQuality    = This->sInfo.dwQuality;
310       icFrames.lDataRate   = pco->dwBytesPerSecond;
311       icFrames.lKeyRate    = This->lKeyFrameEvery;
312       icFrames.dwRate      = This->sInfo.dwRate;
313       icFrames.dwScale     = This->sInfo.dwScale;
314       ICSendMessage(This->hic, ICM_COMPRESS_FRAMES_INFO,
315                     (LPARAM)&icFrames, (LPARAM)sizeof(icFrames));
316     }
317   } else
318     This->sInfo.fccHandler = comptypeDIB;
319
320   return AVIERR_OK;
321 }
322
323 static HRESULT WINAPI ICMStream_fnInfo(IAVIStream *iface,LPAVISTREAMINFOW psi,
324                                         LONG size)
325 {
326   ICOM_THIS(IAVIStreamImpl,iface);
327
328   TRACE("(%p,%p,%ld)\n", iface, psi, size);
329
330   if (psi == NULL)
331     return AVIERR_BADPARAM;
332   if (size < 0)
333     return AVIERR_BADSIZE;
334
335   memcpy(psi, &This->sInfo, min((DWORD)size, sizeof(This->sInfo)));
336
337   if ((DWORD)size < sizeof(This->sInfo))
338     return AVIERR_BUFFERTOOSMALL;
339   return AVIERR_OK;
340 }
341
342 static LONG WINAPI ICMStream_fnFindSample(IAVIStream *iface, LONG pos,
343                                            LONG flags)
344 {
345   ICOM_THIS(IAVIStreamImpl,iface);
346
347   TRACE("(%p,%ld,0x%08lX)\n",iface,pos,flags);
348
349   if (flags & FIND_FROM_START) {
350     pos = This->sInfo.dwStart;
351     flags &= ~(FIND_FROM_START|FIND_PREV);
352     flags |= FIND_NEXT;
353   }
354
355   if (flags & FIND_RET)
356     WARN(": FIND_RET flags will be ignored!\n");
357
358   if (flags & FIND_KEY) {
359     if (This->hic == NULL)
360       return pos; /* we decompress so every frame is a keyframe */
361
362     if (flags & FIND_PREV) {
363       /* need to read old or new frames? */
364       if (This->lLastKey <= pos || pos < This->lCurrent)
365         IAVIStream_Read(iface, pos, 1, NULL, 0, NULL, NULL);
366
367       return This->lLastKey;
368     }
369   } else if (flags & FIND_ANY) {
370     return pos; /* We really don't know, reread is to expensive, so guess. */
371   } else if (flags & FIND_FORMAT) {
372     if (flags & FIND_PREV)
373       return 0;
374   }
375
376   return -1;
377 }
378
379 static HRESULT WINAPI ICMStream_fnReadFormat(IAVIStream *iface, LONG pos,
380                                               LPVOID format, LONG *formatsize)
381 {
382   ICOM_THIS(IAVIStreamImpl,iface);
383
384   LPBITMAPINFOHEADER lpbi;
385   HRESULT            hr;
386
387   TRACE("(%p,%ld,%p,%p)\n", iface, pos, format, formatsize);
388
389   if (formatsize == NULL)
390     return AVIERR_BADPARAM;
391
392   if (This->pg == NULL) {
393     hr = AVIFILE_OpenGetFrame(This);
394
395     if (FAILED(hr))
396       return hr;
397   }
398
399   lpbi = (LPBITMAPINFOHEADER)AVIStreamGetFrame(This->pg, pos);
400   if (lpbi == NULL)
401     return AVIERR_MEMORY;
402
403   if (This->hic == NULL) {
404     LONG size = lpbi->biSize + lpbi->biClrUsed * sizeof(RGBQUAD);
405
406     if (size > 0) {
407       if (This->sInfo.dwSuggestedBufferSize < lpbi->biSizeImage)
408         This->sInfo.dwSuggestedBufferSize = lpbi->biSizeImage;
409
410       This->cbOutput = size;
411       if (format != NULL) {
412         if (This->lpbiOutput != NULL)
413           memcpy(format, This->lpbiOutput, min(*formatsize, This->cbOutput));
414         else
415           memcpy(format, lpbi, min(*formatsize, size));
416       }
417     }
418   } else if (format != NULL)
419     memcpy(format, This->lpbiOutput, min(*formatsize, This->cbOutput));
420
421   if (*formatsize < This->cbOutput)
422     hr = AVIERR_BUFFERTOOSMALL;
423   else
424     hr = AVIERR_OK;
425
426   *formatsize = This->cbOutput;
427   return hr;
428 }
429
430 static HRESULT WINAPI ICMStream_fnSetFormat(IAVIStream *iface, LONG pos,
431                                              LPVOID format, LONG formatsize)
432 {
433   ICOM_THIS(IAVIStreamImpl,iface);
434
435   TRACE("(%p,%ld,%p,%ld)\n", iface, pos, format, formatsize);
436
437   /* check parameters */
438   if (format == NULL || formatsize <= 0)
439     return AVIERR_BADPARAM;
440
441   /* We can only accept RGB data for writing */
442   if (((LPBITMAPINFOHEADER)format)->biCompression != BI_RGB) {
443     WARN(": need RGB data as input\n");
444     return AVIERR_UNSUPPORTED;
445   }
446
447   /* Input format already known?
448    * Changing of palette is supported, but be quiet if it's the same */
449   if (This->lpbiInput != NULL) {
450     if (This->cbInput != formatsize)
451       return AVIERR_UNSUPPORTED;
452
453     if (memcmp(format, This->lpbiInput, formatsize) == 0)
454       return AVIERR_OK;
455   }
456
457   /* Does the nested stream support writing? */
458   if ((This->sInfo.dwCaps & AVIFILECAPS_CANWRITE) == 0)
459     return AVIERR_READONLY;
460
461   /* check if frame is already written */
462   if (This->sInfo.dwLength + This->sInfo.dwStart > pos)
463     return AVIERR_UNSUPPORTED;
464
465   /* check if we should compress */
466   if (This->sInfo.fccHandler == 0 ||
467       This->sInfo.fccHandler == mmioFOURCC('N','O','N','E'))
468     This->sInfo.fccHandler = comptypeDIB;
469
470   /* only pass through? */
471   if (This->sInfo.fccHandler == comptypeDIB)
472     return IAVIStream_SetFormat(This->pStream, pos, format, formatsize);
473
474   /* initial format setting? */
475   if (This->lpbiInput == NULL) {
476     LONG size;
477
478     assert(This->hic != NULL);
479
480     /* get memory for input format */
481     This->lpbiInput = (LPBITMAPINFOHEADER)GlobalAllocPtr(GHND, formatsize);
482     if (This->lpbiInput == NULL)
483       return AVIERR_MEMORY;
484     This->cbInput = formatsize;
485     memcpy(This->lpbiInput, format, formatsize);
486
487     /* get output format */
488     size = ICCompressGetFormatSize(This->hic, This->lpbiInput);
489     if (size < sizeof(BITMAPINFOHEADER))
490       return AVIERR_COMPRESSOR;
491     This->lpbiOutput = (LPBITMAPINFOHEADER)GlobalAllocPtr(GHND, size);
492     if (This->lpbiOutput == NULL)
493       return AVIERR_MEMORY;
494     This->cbOutput = size;
495     if (ICCompressGetFormat(This->hic,This->lpbiInput,This->lpbiOutput) < S_OK)
496       return AVIERR_COMPRESSOR;
497
498     /* update AVISTREAMINFO structure */
499     This->sInfo.rcFrame.right  =
500       This->sInfo.rcFrame.left + This->lpbiOutput->biWidth;
501     This->sInfo.rcFrame.bottom =
502       This->sInfo.rcFrame.top  + This->lpbiOutput->biHeight;
503
504     /* prepare codec for compression */
505     if (ICCompressBegin(This->hic, This->lpbiInput, This->lpbiOutput) != S_OK)
506       return AVIERR_COMPRESSOR;
507
508     /* allocate memory for compressed frame */
509     size = ICCompressGetSize(This->hic, This->lpbiInput, This->lpbiOutput);
510     This->lpbiCur =
511       (LPBITMAPINFOHEADER)GlobalAllocPtr(GMEM_MOVEABLE, This->cbOutput + size);
512     if (This->lpbiCur == NULL)
513       return AVIERR_MEMORY;
514     memcpy(This->lpbiCur, This->lpbiOutput, This->cbOutput);
515     This->lpCur = DIBPTR(This->lpbiCur);
516
517     /* allocate memory for last frame if needed */
518     if (This->lKeyFrameEvery != 1 &&
519         (This->dwICMFlags & VIDCF_FASTTEMPORALC) == 0) {
520       size = ICDecompressGetFormatSize(This->hic, This->lpbiOutput);
521       This->lpbiPrev = (LPBITMAPINFOHEADER)GlobalAllocPtr(GHND, size);
522       if (This->lpbiPrev == NULL)
523         return AVIERR_MEMORY;
524       if (ICDecompressGetFormat(This->hic, This->lpbiOutput, This->lpbiPrev) < S_OK)
525         return AVIERR_COMPRESSOR;
526
527       if (This->lpbiPrev->biSizeImage == 0) {
528         This->lpbiPrev->biSizeImage =
529           DIBWIDTHBYTES(*This->lpbiPrev) * This->lpbiPrev->biHeight;
530       }
531
532       /* get memory for format and picture */
533       size += This->lpbiPrev->biSizeImage;
534       This->lpbiPrev =
535        (LPBITMAPINFOHEADER)GlobalReAllocPtr(This->lpbiPrev,size,GMEM_MOVEABLE);
536       if (This->lpbiPrev == NULL)
537         return AVIERR_MEMORY;
538       This->lpPrev = DIBPTR(This->lpbiPrev);
539
540       /* prepare codec also for decompression */
541       if (ICDecompressBegin(This->hic,This->lpbiOutput,This->lpbiPrev) != S_OK)
542         return AVIERR_COMPRESSOR;
543     }
544   } else {
545     /* format change -- check that's only the palette */
546     LPBITMAPINFOHEADER lpbi = (LPBITMAPINFOHEADER)format;
547
548     if (lpbi->biSize != This->lpbiInput->biSize ||
549         lpbi->biWidth != This->lpbiInput->biWidth ||
550         lpbi->biHeight != This->lpbiInput->biHeight ||
551         lpbi->biBitCount != This->lpbiInput->biBitCount ||
552         lpbi->biPlanes != This->lpbiInput->biPlanes ||
553         lpbi->biCompression != This->lpbiInput->biCompression ||
554         lpbi->biClrUsed != This->lpbiInput->biClrUsed)
555       return AVIERR_UNSUPPORTED;
556
557     /* get new output format */
558     if (ICCompressGetFormat(This->hic, lpbi, This->lpbiOutput) < S_OK)
559       return AVIERR_BADFORMAT;
560
561     /* restart compression */
562     ICCompressEnd(This->hic);
563     if (ICCompressBegin(This->hic, lpbi, This->lpbiOutput) != S_OK)
564       return AVIERR_COMPRESSOR;
565
566     /* check if we need to restart decompresion also */
567     if (This->lKeyFrameEvery != 1 &&
568         (This->dwICMFlags & VIDCF_FASTTEMPORALC) == 0) {
569       ICDecompressEnd(This->hic);
570       if (ICDecompressGetFormat(This->hic,This->lpbiOutput,This->lpbiPrev) < S_OK)
571         return AVIERR_COMPRESSOR;
572       if (ICDecompressBegin(This->hic,This->lpbiOutput,This->lpbiPrev) != S_OK)
573         return AVIERR_COMPRESSOR;
574     }
575   }
576
577   /* tell nested stream the new format */
578   return IAVIStream_SetFormat(This->pStream, pos,
579                               This->lpbiOutput, This->cbOutput);
580 }
581
582 static HRESULT WINAPI ICMStream_fnRead(IAVIStream *iface, LONG start,
583                                         LONG samples, LPVOID buffer,
584                                         LONG buffersize, LPLONG bytesread,
585                                         LPLONG samplesread)
586 {
587   ICOM_THIS(IAVIStreamImpl,iface);
588
589   LPBITMAPINFOHEADER lpbi;
590
591   TRACE("(%p,%ld,%ld,%p,%ld,%p,%p)\n", iface, start, samples, buffer,
592         buffersize, bytesread, samplesread);
593
594   /* clear return parameters if given */
595   if (bytesread != NULL)
596     *bytesread = 0;
597   if (samplesread != NULL)
598     *samplesread = 0;
599
600   if (samples == 0)
601     return AVIERR_OK;
602
603   /* check parameters */
604   if (samples != 1 && (bytesread == NULL && samplesread == NULL))
605     return AVIERR_BADPARAM;
606   if (samples == -1) /* read as much as we could */
607     samples = 1;
608
609   if (This->pg == NULL) {
610     HRESULT hr = AVIFILE_OpenGetFrame(This);
611
612     if (FAILED(hr))
613       return hr;
614   }
615
616   /* compress or decompress? */
617   if (This->hic == NULL) {
618     /* decompress */
619     lpbi = (LPBITMAPINFOHEADER)AVIStreamGetFrame(This->pg, start);
620     if (lpbi == NULL)
621       return AVIERR_MEMORY;
622
623     if (buffer != NULL && buffersize > 0) {
624       /* check buffersize */
625       if (buffersize < lpbi->biSizeImage)
626         return AVIERR_BUFFERTOOSMALL;
627
628       memcpy(buffer, DIBPTR(lpbi), lpbi->biSizeImage);
629     }
630
631     /* fill out return parameters if given */
632     if (bytesread != NULL)
633       *bytesread = lpbi->biSizeImage;
634   } else {
635     /* compress */
636     if (This->lCurrent > start)
637       AVIFILE_Reset(This);
638
639     while (start > This->lCurrent) {
640       HRESULT hr;
641
642       lpbi = (LPBITMAPINFOHEADER)AVIStreamGetFrame(This->pg, ++This->lCurrent);
643       if (lpbi == NULL) {
644         AVIFILE_Reset(This);
645         return AVIERR_MEMORY;
646       }
647
648       hr = AVIFILE_EncodeFrame(This, lpbi, DIBPTR(lpbi));
649       if (FAILED(hr)) {
650         AVIFILE_Reset(This);
651         return hr;
652       }
653     }
654
655     if (buffer != NULL && buffersize > 0) {
656       /* check buffersize */
657       if (This->lpbiCur->biSizeImage > buffersize)
658         return AVIERR_BUFFERTOOSMALL;
659
660       memcpy(buffer, This->lpCur, This->lpbiCur->biSizeImage);
661     }
662
663     /* fill out return parameters if given */
664     if (bytesread != NULL)
665       *bytesread = This->lpbiCur->biSizeImage;
666   }
667
668   /* fill out return parameters if given */
669   if (samplesread != NULL)
670     *samplesread = 1;
671
672   return AVIERR_OK;
673 }
674
675 static HRESULT WINAPI ICMStream_fnWrite(IAVIStream *iface, LONG start,
676                                          LONG samples, LPVOID buffer,
677                                          LONG buffersize, DWORD flags,
678                                          LPLONG sampwritten,
679                                          LPLONG byteswritten)
680 {
681   ICOM_THIS(IAVIStreamImpl,iface);
682
683   HRESULT hr;
684
685   TRACE("(%p,%ld,%ld,%p,%ld,0x%08lX,%p,%p)\n", iface, start, samples,
686         buffer, buffersize, flags, sampwritten, byteswritten);
687
688   /* clear return parameters if given */
689   if (sampwritten != NULL)
690     *sampwritten = 0;
691   if (byteswritten != NULL)
692     *byteswritten = 0;
693
694   /* check parameters */
695   if (buffer == NULL && (buffersize > 0 || samples > 0))
696     return AVIERR_BADPARAM;
697
698   if (This->sInfo.fccHandler == comptypeDIB) {
699     /* only pass through */
700     flags |= AVIIF_KEYFRAME;
701
702     return IAVIStream_Write(This->pStream, start, samples, buffer, buffersize,
703                             flags, sampwritten, byteswritten);
704   } else {
705     /* compress data before writing to pStream */
706     if (samples != 1 && (sampwritten == NULL && byteswritten == NULL))
707       return AVIERR_UNSUPPORTED;
708
709     This->lCurrent = start;
710     hr = AVIFILE_EncodeFrame(This, This->lpbiInput, buffer);
711     if (FAILED(hr))
712       return hr;
713
714     if (This->lLastKey == start)
715       flags |= AVIIF_KEYFRAME;
716
717     return IAVIStream_Write(This->pStream, start, samples, This->lpCur,
718                             This->lpbiCur->biSizeImage, flags, byteswritten,
719                             sampwritten);
720   }
721 }
722
723 static HRESULT WINAPI ICMStream_fnDelete(IAVIStream *iface, LONG start,
724                                           LONG samples)
725 {
726   ICOM_THIS(IAVIStreamImpl,iface);
727
728   TRACE("(%p,%ld,%ld)\n", iface, start, samples);
729
730   return IAVIStream_Delete(This->pStream, start, samples);
731 }
732
733 static HRESULT WINAPI ICMStream_fnReadData(IAVIStream *iface, DWORD fcc,
734                                             LPVOID lp, LPLONG lpread)
735 {
736   ICOM_THIS(IAVIStreamImpl,iface);
737
738   TRACE("(%p,0x%08lX,%p,%p)\n", iface, fcc, lp, lpread);
739
740   assert(This->pStream != NULL);
741
742   return IAVIStream_ReadData(This->pStream, fcc, lp, lpread);
743 }
744
745 static HRESULT WINAPI ICMStream_fnWriteData(IAVIStream *iface, DWORD fcc,
746                                              LPVOID lp, LONG size)
747 {
748   ICOM_THIS(IAVIStreamImpl,iface);
749
750   TRACE("(%p,0x%08lx,%p,%ld)\n", iface, fcc, lp, size);
751
752   assert(This->pStream != NULL);
753
754   return IAVIStream_WriteData(This->pStream, fcc, lp, size);
755 }
756
757 static HRESULT WINAPI ICMStream_fnSetInfo(IAVIStream *iface,
758                                            LPAVISTREAMINFOW info, LONG infolen)
759 {
760   FIXME("(%p,%p,%ld): stub\n", iface, info, infolen);
761
762   return E_FAIL;
763 }
764
765 /***********************************************************************/
766
767 static HRESULT AVIFILE_EncodeFrame(IAVIStreamImpl *This,
768                                    LPBITMAPINFOHEADER lpbi, LPVOID lpBits)
769 {
770   DWORD dwMinQual, dwMaxQual, dwCurQual;
771   DWORD dwRequest;
772   DWORD icmFlags = 0;
773   DWORD idxFlags = 0;
774   BOOL  bDecreasedQual = FALSE;
775   BOOL  doSizeCheck;
776   BOOL  noPrev;
777
778   /* make lKeyFrameEvery and at start a keyframe */
779   if ((This->lKeyFrameEvery != 0 &&
780        (This->lCurrent - This->lLastKey) >= This->lKeyFrameEvery) ||
781       This->lCurrent == This->sInfo.dwStart) {
782     idxFlags = AVIIF_KEYFRAME;
783     icmFlags = ICCOMPRESS_KEYFRAME;
784   }
785
786   if (This->lKeyFrameEvery != 0) {
787     if (This->lCurrent == This->sInfo.dwStart) {
788       if (idxFlags & AVIIF_KEYFRAME) {
789         /* for keyframes allow to consume all unused bytes */
790         dwRequest = This->dwBytesPerFrame + This->dwUnusedBytes;
791         This->dwUnusedBytes = 0;
792       } else {
793         /* for non-keyframes only allow something of the unused bytes to be consumed */
794         DWORD tmp1 = 0;
795         DWORD tmp2;
796
797         if (This->dwBytesPerFrame >= This->dwUnusedBytes)
798           tmp1 = This->dwBytesPerFrame / This->lKeyFrameEvery;
799         tmp2 = (This->dwUnusedBytes + tmp1) / This->lKeyFrameEvery;
800
801         dwRequest = This->dwBytesPerFrame - tmp1 + tmp2;
802         This->dwUnusedBytes -= tmp2;
803       }
804     } else
805       dwRequest = MAX_FRAMESIZE;
806   } else {
807     /* only one keyframe at start desired */
808     if (This->lCurrent == This->sInfo.dwStart) {
809       dwRequest = This->dwBytesPerFrame + This->dwUnusedBytes;
810       This->dwUnusedBytes = 0;
811     } else
812       dwRequest = MAX_FRAMESIZE;
813   }
814
815   /* must we check for framesize to gain requested
816    * datarate or could we trust codec? */
817   doSizeCheck = (dwRequest != 0 && ((This->dwICMFlags & (VIDCF_CRUNCH|VIDCF_QUALITY)) == 0));
818
819   dwMaxQual = dwCurQual = This->sInfo.dwQuality;
820   dwMinQual = ICQUALITY_LOW;
821
822   noPrev = TRUE;
823   if ((icmFlags & ICCOMPRESS_KEYFRAME) == 0 && 
824       (This->dwICMFlags & VIDCF_FASTTEMPORALC) == 0)
825     noPrev = FALSE;
826
827   do {
828     DWORD   idxCkid = 0;
829     HRESULT hr;
830
831     hr = ICCompress(This->hic,icmFlags,This->lpbiCur,This->lpCur,lpbi,lpBits,
832                     &idxCkid, &idxFlags, This->lCurrent, dwRequest, dwCurQual,
833                     noPrev ? NULL:This->lpbiPrev, noPrev ? NULL:This->lpPrev);
834     if (hr == ICERR_NEWPALETTE) {
835       FIXME(": codec has changed palette -- unhandled!\n");
836     } else if (hr != ICERR_OK)
837       return AVIERR_COMPRESSOR;
838
839     /* need to check for framesize */
840     if (! doSizeCheck)
841       break;
842
843     if (dwRequest >= This->lpbiCur->biSizeImage) {
844       /* frame is smaller -- try to maximize quality */
845       if (dwMaxQual - dwCurQual > 10) {
846         DWORD tmp = dwRequest / 8;
847
848         if (tmp < MAX_FRAMESIZE_DIFF)
849           tmp = MAX_FRAMESIZE_DIFF;
850
851         if (tmp < dwRequest - This->lpbiCur->biSizeImage && bDecreasedQual) {
852           tmp = dwCurQual;
853           dwCurQual = (dwMinQual + dwMaxQual) / 2;
854           dwMinQual = tmp;
855           continue;
856         }
857       } else
858         break;
859     } else if (dwMaxQual - dwMinQual <= 1) {
860       break;
861     } else {
862       dwMaxQual = dwCurQual;
863
864       if (bDecreasedQual || dwCurQual == This->dwLastQuality)
865         dwCurQual = (dwMinQual + dwMaxQual) / 2;
866       else
867         FIXME(": no new quality computed min=%lu cur=%lu max=%lu last=%lu\n",
868               dwMinQual, dwCurQual, dwMaxQual, This->dwLastQuality);
869
870       bDecreasedQual = TRUE;
871     }
872   } while (TRUE);
873
874   /* remember some values */
875   This->dwLastQuality = dwCurQual;
876   This->dwUnusedBytes = dwRequest - This->lpbiCur->biSizeImage;
877   if (icmFlags & ICCOMPRESS_KEYFRAME)
878     This->lLastKey = This->lCurrent;
879
880   /* Does we manage previous frame? */
881   if (This->lpPrev != NULL && This->lKeyFrameEvery != 1)
882     ICDecompress(This->hic, 0, This->lpbiCur, This->lpCur,
883                  This->lpbiPrev, This->lpPrev);
884
885   return AVIERR_OK;
886 }
887
888 static HRESULT AVIFILE_OpenGetFrame(IAVIStreamImpl *This)
889 {
890   LPBITMAPINFOHEADER lpbi;
891   DWORD              size;
892
893   /* pre-conditions */
894   assert(This != NULL);
895   assert(This->pStream != NULL);
896   assert(This->pg == NULL);
897
898   This->pg = AVIStreamGetFrameOpen(This->pStream, NULL);
899   if (This->pg == NULL)
900     return AVIERR_ERROR;
901
902   /* When we only decompress this is enough */
903   if (This->sInfo.fccHandler == comptypeDIB)
904     return AVIERR_OK;
905
906   assert(This->hic != NULL);
907   assert(This->lpbiOutput == NULL);
908
909   /* get input format */
910   lpbi = (LPBITMAPINFOHEADER)AVIStreamGetFrame(This->pg, This->sInfo.dwStart);
911   if (lpbi == NULL)
912     return AVIERR_MEMORY;
913
914   /* get memory for output format */
915   size = ICCompressGetFormatSize(This->hic, lpbi);
916   if ((LONG)size < (LONG)sizeof(BITMAPINFOHEADER))
917     return AVIERR_COMPRESSOR;
918   This->lpbiOutput = (LPBITMAPINFOHEADER)GlobalAllocPtr(GHND, size);
919   if (This->lpbiOutput == NULL)
920     return AVIERR_MEMORY;
921   This->cbOutput = size;
922
923   if (ICCompressGetFormat(This->hic, lpbi, This->lpbiOutput) < S_OK)
924     return AVIERR_BADFORMAT;
925
926   /* update AVISTREAMINFO structure */
927   This->sInfo.rcFrame.right  =
928     This->sInfo.rcFrame.left + This->lpbiOutput->biWidth;
929   This->sInfo.rcFrame.bottom =
930     This->sInfo.rcFrame.top  + This->lpbiOutput->biHeight;
931   This->sInfo.dwSuggestedBufferSize =
932     ICCompressGetSize(This->hic, lpbi, This->lpbiOutput);
933
934   /* prepare codec for compression */
935   if (ICCompressBegin(This->hic, lpbi, This->lpbiOutput) != S_OK)
936     return AVIERR_COMPRESSOR;
937
938   /* allocate memory for current frame */
939   size += This->sInfo.dwSuggestedBufferSize;
940   This->lpbiCur = (LPBITMAPINFOHEADER)GlobalAllocPtr(GMEM_MOVEABLE, size);
941   if (This->lpbiCur == NULL)
942     return AVIERR_MEMORY;
943   memcpy(This->lpbiCur, This->lpbiOutput, This->cbOutput);
944   This->lpCur = DIBPTR(This->lpbiCur);
945
946   /* allocate memory for last frame if needed */
947   if (This->lKeyFrameEvery != 1 &&
948       (This->dwICMFlags & VIDCF_FASTTEMPORALC) == 0) {
949     size = ICDecompressGetFormatSize(This->hic, This->lpbiOutput);
950     This->lpbiPrev = (LPBITMAPINFOHEADER)GlobalAllocPtr(GHND, size);
951     if (This->lpbiPrev == NULL)
952       return AVIERR_MEMORY;
953     if (ICDecompressGetFormat(This->hic, This->lpbiOutput, This->lpbiPrev) < S_OK)
954       return AVIERR_COMPRESSOR;
955
956     if (This->lpbiPrev->biSizeImage == 0) {
957       This->lpbiPrev->biSizeImage =
958         DIBWIDTHBYTES(*This->lpbiPrev) * This->lpbiPrev->biHeight;
959     }
960
961     /* get memory for format and picture */
962     size += This->lpbiPrev->biSizeImage;
963     This->lpbiPrev =
964       (LPBITMAPINFOHEADER)GlobalReAllocPtr(This->lpbiPrev,size,GMEM_MOVEABLE);
965     if (This->lpbiPrev == NULL)
966       return AVIERR_MEMORY;
967     This->lpPrev = DIBPTR(This->lpbiPrev);
968
969     /* prepare codec also for decompression */
970     if (ICDecompressBegin(This->hic,This->lpbiOutput,This->lpbiPrev) != S_OK)
971       return AVIERR_COMPRESSOR;
972   }
973
974   return AVIERR_OK;
975 }