avifil32: Use HeapAlloc instead of LocalAlloc. Make sure to free the memory.
[wine] / dlls / avifil32 / getframe.c
1 /*
2  * Copyright 2002-2003 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 "winnls.h"
26 #include "wingdi.h"
27 #include "winuser.h"
28 #include "vfw.h"
29
30 #include "avifile_private.h"
31
32 #include "wine/debug.h"
33
34 WINE_DEFAULT_DEBUG_CHANNEL(avifile);
35
36 #ifndef DIBPTR
37 #define DIBPTR(lp)      ((LPBYTE)(lp) + (lp)->biSize + \
38                          (lp)->biClrUsed * sizeof(RGBQUAD))
39 #endif
40
41 /***********************************************************************/
42
43 static HRESULT WINAPI IGetFrame_fnQueryInterface(IGetFrame *iface,
44                                                  REFIID refiid, LPVOID *obj);
45 static ULONG   WINAPI IGetFrame_fnAddRef(IGetFrame *iface);
46 static ULONG   WINAPI IGetFrame_fnRelease(IGetFrame *iface);
47 static LPVOID  WINAPI IGetFrame_fnGetFrame(IGetFrame *iface, LONG lPos);
48 static HRESULT WINAPI IGetFrame_fnBegin(IGetFrame *iface, LONG lStart,
49                                         LONG lEnd, LONG lRate);
50 static HRESULT WINAPI IGetFrame_fnEnd(IGetFrame *iface);
51 static HRESULT WINAPI IGetFrame_fnSetFormat(IGetFrame *iface,
52                                             LPBITMAPINFOHEADER lpbi,
53                                             LPVOID lpBits, INT x, INT y,
54                                             INT dx, INT dy);
55
56 static const struct IGetFrameVtbl igetframeVtbl = {
57   IGetFrame_fnQueryInterface,
58   IGetFrame_fnAddRef,
59   IGetFrame_fnRelease,
60   IGetFrame_fnGetFrame,
61   IGetFrame_fnBegin,
62   IGetFrame_fnEnd,
63   IGetFrame_fnSetFormat
64 };
65
66 typedef struct _IGetFrameImpl {
67   /* IUnknown stuff */
68   const IGetFrameVtbl *lpVtbl;
69   LONG               ref;
70
71   /* IGetFrame stuff */
72   BOOL               bFixedStream;
73   PAVISTREAM         pStream;
74
75   LPVOID             lpInBuffer;
76   LONG               cbInBuffer;
77   LPBITMAPINFOHEADER lpInFormat;
78   LONG               cbInFormat;
79
80   LONG               lCurrentFrame;
81   LPBITMAPINFOHEADER lpOutFormat;
82   LPVOID             lpOutBuffer;
83
84   HIC                hic;
85   BOOL               bResize;
86   DWORD              x;
87   DWORD              y;
88   DWORD              dx;
89   DWORD              dy;
90
91   BOOL               bFormatChanges;
92   DWORD              dwFormatChangeCount;
93   DWORD              dwEditCount;
94 } IGetFrameImpl;
95
96 /***********************************************************************/
97
98 static void AVIFILE_CloseCompressor(IGetFrameImpl *This)
99 {
100   if (This->lpOutFormat != NULL && This->lpInFormat != This->lpOutFormat) {
101     HeapFree(GetProcessHeap(), 0, This->lpOutFormat);
102     This->lpOutFormat = NULL;
103   }
104   if (This->lpInFormat != NULL) {
105     HeapFree(GetProcessHeap(), 0, This->lpInFormat);
106     This->lpInFormat = NULL;
107   }
108   if (This->hic != NULL) {
109     if (This->bResize)
110       ICDecompressExEnd(This->hic);
111     else
112       ICDecompressEnd(This->hic);
113     ICClose(This->hic);
114     This->hic = NULL;
115   }
116 }
117
118 PGETFRAME AVIFILE_CreateGetFrame(PAVISTREAM pStream)
119 {
120   IGetFrameImpl *pg;
121
122   /* check parameter */
123   if (pStream == NULL)
124     return NULL;
125
126   pg = HeapAlloc(GetProcessHeap(), 0, sizeof(IGetFrameImpl));
127   if (pg != NULL) {
128     pg->lpVtbl        = &igetframeVtbl;
129     pg->ref           = 1;
130     pg->lCurrentFrame = -1;
131     pg->pStream       = pStream;
132     IAVIStream_AddRef(pStream);
133   }
134
135   return (PGETFRAME)pg;
136 }
137
138 static HRESULT WINAPI IGetFrame_fnQueryInterface(IGetFrame *iface,
139                                                  REFIID refiid, LPVOID *obj)
140 {
141   IGetFrameImpl *This = (IGetFrameImpl *)iface;
142
143   TRACE("(%p,%s,%p)\n", This, debugstr_guid(refiid), obj);
144
145   if (IsEqualGUID(&IID_IUnknown, refiid) ||
146       IsEqualGUID(&IID_IGetFrame, refiid)) {
147     *obj = iface;
148     return S_OK;
149   }
150
151   return OLE_E_ENUM_NOMORE;
152 }
153
154 static ULONG   WINAPI IGetFrame_fnAddRef(IGetFrame *iface)
155 {
156   IGetFrameImpl *This = (IGetFrameImpl *)iface;
157   ULONG ref = InterlockedIncrement(&This->ref);
158
159   TRACE("(%p)\n", iface);
160
161   return ref;
162 }
163
164 static ULONG   WINAPI IGetFrame_fnRelease(IGetFrame *iface)
165 {
166   IGetFrameImpl *This = (IGetFrameImpl *)iface;
167   ULONG ref = InterlockedDecrement(&This->ref);
168
169   TRACE("(%p)\n", iface);
170
171   if (!ref) {
172     AVIFILE_CloseCompressor(This);
173     if (This->pStream != NULL) {
174       IAVIStream_Release(This->pStream);
175       This->pStream = NULL;
176     }
177
178     HeapFree(GetProcessHeap(), 0, iface);
179     return 0;
180   }
181
182   return ref;
183 }
184
185 static LPVOID  WINAPI IGetFrame_fnGetFrame(IGetFrame *iface, LONG lPos)
186 {
187   IGetFrameImpl *This = (IGetFrameImpl *)iface;
188
189   LONG readBytes;
190   LONG readSamples;
191
192   TRACE("(%p,%ld)\n", iface, lPos);
193
194   /* We don't want negative start values! -- marks invalid buffer content */
195   if (lPos < 0)
196     return NULL;
197
198   /* check state */
199   if (This->pStream == NULL)
200     return NULL;
201   if (This->lpInFormat == NULL)
202     return NULL;
203
204   /* Could stream have changed? */
205   if (! This->bFixedStream) {
206     AVISTREAMINFOW sInfo;
207
208     IAVIStream_Info(This->pStream, &sInfo, sizeof(sInfo));
209
210     if (sInfo.dwEditCount != This->dwEditCount) {
211       This->dwEditCount   = sInfo.dwEditCount;
212       This->lCurrentFrame = -1;
213     }
214
215     if (sInfo.dwFormatChangeCount != This->dwFormatChangeCount) {
216       /* stream has changed */
217       if (This->lpOutFormat != NULL) {
218         BITMAPINFOHEADER bi;
219
220         memcpy(&bi, This->lpOutFormat, sizeof(bi));
221         AVIFILE_CloseCompressor(This);
222
223         if (FAILED(IGetFrame_SetFormat(iface, &bi, NULL, 0, 0, -1, -1))) {
224           if (FAILED(IGetFrame_SetFormat(iface, NULL, NULL, 0, 0, -1, -1)))
225             return NULL;
226         }
227       } else if (FAILED(IGetFrame_SetFormat(iface, NULL, NULL, 0, 0, -1, -1)))
228         return NULL;
229     }
230   }
231
232   if (lPos != This->lCurrentFrame) {
233     LONG lNext = IAVIStream_FindSample(This->pStream,lPos,FIND_KEY|FIND_PREV);
234
235     if (lNext == -1)
236       return NULL; /* frame doesn't exist */
237     if (lNext <= This->lCurrentFrame && This->lCurrentFrame < lPos)
238       lNext = This->lCurrentFrame + 1;
239
240     for (; lNext <= lPos; lNext++) {
241       /* new format for this frame? */
242       if (This->bFormatChanges) {
243         IAVIStream_ReadFormat(This->pStream, lNext,
244                               This->lpInFormat, &This->cbInFormat);
245         if (This->lpOutFormat != NULL) {
246           if (This->lpOutFormat->biBitCount <= 8)
247             ICDecompressGetPalette(This->hic, This->lpInFormat,
248                                    This->lpOutFormat);
249         }
250       }
251
252       /* read input frame */
253       while (FAILED(AVIStreamRead(This->pStream, lNext, 1, This->lpInBuffer,
254                                   This->cbInBuffer, &readBytes, &readSamples))) {
255         /* not enough memory for input buffer? */
256         readBytes = 0;
257         if (FAILED(AVIStreamSampleSize(This->pStream, lNext, &readBytes)))
258           return NULL; /* bad thing, but bad things will happen */
259         if (readBytes <= 0) {
260           ERR(": IAVIStream::REad doesn't return needed bytes!\n");
261           return NULL;
262         }
263
264         /* IAVIStream::Read failed because of other reasons not buffersize? */
265         if (This->cbInBuffer >= readBytes)
266           break;
267         This->cbInBuffer = This->cbInFormat + readBytes;
268         This->lpInFormat = HeapReAlloc(GetProcessHeap(), 0, This->lpInFormat, This->cbInBuffer);
269         if (This->lpInFormat == NULL)
270           return NULL; /* out of memory */
271         This->lpInBuffer = (BYTE*)This->lpInFormat + This->cbInFormat;
272       }
273
274       if (readSamples != 1) {
275         ERR(": no frames read\n");
276         return NULL;
277       }
278       if (readBytes != 0) {
279         This->lpInFormat->biSizeImage = readBytes;
280
281         /* nothing to decompress? */
282         if (This->hic == NULL) {
283           This->lCurrentFrame = lPos;
284           return This->lpInFormat;
285         }
286
287         if (This->bResize) {
288           ICDecompressEx(This->hic,0,This->lpInFormat,This->lpInBuffer,0,0,
289                          This->lpInFormat->biWidth,This->lpInFormat->biHeight,
290                          This->lpOutFormat,This->lpOutBuffer,This->x,This->y,
291                          This->dx,This->dy);
292         } else {
293           ICDecompress(This->hic, 0, This->lpInFormat, This->lpInBuffer,
294                        This->lpOutFormat, This->lpOutBuffer);
295         }
296       }
297     } /* for (lNext < lPos) */
298   } /* if (This->lCurrentFrame != lPos) */
299
300   return (This->hic == NULL ? This->lpInFormat : This->lpOutFormat);
301 }
302
303 static HRESULT WINAPI IGetFrame_fnBegin(IGetFrame *iface, LONG lStart,
304                                         LONG lEnd, LONG lRate)
305 {
306   IGetFrameImpl *This = (IGetFrameImpl *)iface;
307
308   TRACE("(%p,%ld,%ld,%ld)\n", iface, lStart, lEnd, lRate);
309
310   This->bFixedStream = TRUE;
311
312   return (IGetFrame_GetFrame(iface, lStart) ? AVIERR_OK : AVIERR_ERROR);
313 }
314
315 static HRESULT WINAPI IGetFrame_fnEnd(IGetFrame *iface)
316 {
317   IGetFrameImpl *This = (IGetFrameImpl *)iface;
318
319   TRACE("(%p)\n", iface);
320
321   This->bFixedStream = FALSE;
322
323   return AVIERR_OK;
324 }
325
326 static HRESULT WINAPI IGetFrame_fnSetFormat(IGetFrame *iface,
327                                             LPBITMAPINFOHEADER lpbiWanted,
328                                             LPVOID lpBits, INT x, INT y,
329                                             INT dx, INT dy)
330 {
331   IGetFrameImpl *This = (IGetFrameImpl *)iface;
332
333   AVISTREAMINFOW     sInfo;
334   LPBITMAPINFOHEADER lpbi         = lpbiWanted;
335   BOOL               bBestDisplay = FALSE;
336
337   TRACE("(%p,%p,%p,%d,%d,%d,%d)\n", iface, lpbiWanted, lpBits,
338         x, y, dx, dy);
339
340   if (This->pStream == NULL)
341     return AVIERR_ERROR;
342
343   if (lpbiWanted == (LPBITMAPINFOHEADER)AVIGETFRAMEF_BESTDISPLAYFMT) {
344     lpbi = NULL;
345     bBestDisplay = TRUE;
346   }
347
348   IAVIStream_Info(This->pStream, &sInfo, sizeof(sInfo));
349   if (sInfo.fccType != streamtypeVIDEO)
350     return AVIERR_UNSUPPORTED;
351
352   This->bFormatChanges =
353     (sInfo.dwFlags & AVISTREAMINFO_FORMATCHANGES ? TRUE : FALSE );
354   This->dwFormatChangeCount = sInfo.dwFormatChangeCount;
355   This->dwEditCount         = sInfo.dwEditCount;
356   This->lCurrentFrame       = -1;
357
358   /* get input format from stream */
359   if (This->lpInFormat == NULL) {
360     HRESULT hr;
361
362     This->cbInBuffer = (LONG)sInfo.dwSuggestedBufferSize;
363     if (This->cbInBuffer == 0)
364       This->cbInBuffer = 1024;
365
366     IAVIStream_ReadFormat(This->pStream, sInfo.dwStart,
367                           NULL, &This->cbInFormat);
368
369     This->lpInFormat = HeapAlloc(GetProcessHeap(), 0, This->cbInFormat + This->cbInBuffer);
370     if (This->lpInFormat == NULL) {
371       AVIFILE_CloseCompressor(This);
372       return AVIERR_MEMORY;
373     }
374
375     hr = IAVIStream_ReadFormat(This->pStream, sInfo.dwStart, This->lpInFormat, &This->cbInFormat);
376     if (FAILED(hr)) {
377       AVIFILE_CloseCompressor(This);
378       return hr;
379     }
380
381     This->lpInBuffer = ((LPBYTE)This->lpInFormat) + This->cbInFormat;
382   }
383
384   /* check input format */
385   if (This->lpInFormat->biClrUsed == 0 && This->lpInFormat->biBitCount <= 8)
386     This->lpInFormat->biClrUsed = 1u << This->lpInFormat->biBitCount;
387   if (This->lpInFormat->biSizeImage == 0 &&
388       This->lpInFormat->biCompression == BI_RGB) {
389     This->lpInFormat->biSizeImage =
390       DIBWIDTHBYTES(*This->lpInFormat) * This->lpInFormat->biHeight;
391   }
392
393   /* only to pass through? */
394   if (This->lpInFormat->biCompression == BI_RGB && lpBits == NULL) {
395     if (lpbi == NULL || 
396         (lpbi->biCompression == BI_RGB &&
397          lpbi->biWidth == This->lpInFormat->biWidth &&
398          lpbi->biHeight == This->lpInFormat->biHeight &&
399          lpbi->biBitCount == This->lpInFormat->biBitCount)) {
400       This->lpOutFormat = This->lpInFormat;
401       This->lpOutBuffer = DIBPTR(This->lpInFormat);
402       return AVIERR_OK;
403     }
404   }
405
406   /* need memory for output format? */
407   if (This->lpOutFormat == NULL) {
408     This->lpOutFormat =
409       HeapAlloc(GetProcessHeap(), 0, sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD));
410     if (This->lpOutFormat == NULL) {
411       AVIFILE_CloseCompressor(This);
412       return AVIERR_MEMORY;
413     }
414   }
415
416   /* need handle to video compressor */
417   if (This->hic == NULL) {
418     FOURCC fccHandler;
419
420     if (This->lpInFormat->biCompression == BI_RGB)
421       fccHandler = comptypeDIB;
422     else if (This->lpInFormat->biCompression == BI_RLE8)
423       fccHandler = mmioFOURCC('R','L','E',' ');
424     else
425       fccHandler = sInfo.fccHandler;
426
427     if (lpbi != NULL) {
428       if (lpbi->biWidth == 0)
429         lpbi->biWidth = This->lpInFormat->biWidth;
430       if (lpbi->biHeight == 0)
431         lpbi->biHeight = This->lpInFormat->biHeight;
432     }
433
434     This->hic = ICLocate(ICTYPE_VIDEO, fccHandler, This->lpInFormat, lpbi, ICMODE_DECOMPRESS);
435     if (This->hic == NULL) {
436       AVIFILE_CloseCompressor(This);
437       return AVIERR_NOCOMPRESSOR;
438     }
439   }
440
441   /* output format given? */
442   if (lpbi != NULL) {
443     /* check the given output format ... */
444     if (lpbi->biClrUsed == 0 && lpbi->biBitCount <= 8)
445       lpbi->biClrUsed = 1u << lpbi->biBitCount;
446
447     /* ... and remember it */
448     memcpy(This->lpOutFormat, lpbi,
449            lpbi->biSize + lpbi->biClrUsed * sizeof(RGBQUAD));
450     if (lpbi->biBitCount <= 8)
451       ICDecompressGetPalette(This->hic, This->lpInFormat, This->lpOutFormat);
452
453     return AVIERR_OK;
454   } else {
455     if (bBestDisplay) {
456       ICGetDisplayFormat(This->hic, This->lpInFormat,
457                          This->lpOutFormat, 0, dx, dy);
458     } else if (ICDecompressGetFormat(This->hic, This->lpInFormat,
459                                      This->lpOutFormat) < 0) {
460       AVIFILE_CloseCompressor(This);
461       return AVIERR_NOCOMPRESSOR;
462     }
463
464     /* check output format */
465     if (This->lpOutFormat->biClrUsed == 0 &&
466         This->lpOutFormat->biBitCount <= 8)
467       This->lpOutFormat->biClrUsed = 1u << This->lpOutFormat->biBitCount;
468     if (This->lpOutFormat->biSizeImage == 0 &&
469         This->lpOutFormat->biCompression == BI_RGB) {
470       This->lpOutFormat->biSizeImage =
471         DIBWIDTHBYTES(*This->lpOutFormat) * This->lpOutFormat->biHeight;
472     }
473
474     if (lpBits == NULL) {
475       register DWORD size = This->lpOutFormat->biClrUsed * sizeof(RGBQUAD);
476
477       size += This->lpOutFormat->biSize + This->lpOutFormat->biSizeImage;
478       This->lpOutFormat = HeapReAlloc(GetProcessHeap(), 0, This->lpOutFormat, size);
479       if (This->lpOutFormat == NULL) {
480         AVIFILE_CloseCompressor(This);
481         return AVIERR_MEMORY;
482       }
483       This->lpOutBuffer = DIBPTR(This->lpOutFormat);
484     } else
485       This->lpOutBuffer = lpBits;
486
487     /* for user size was irrelevant */
488     if (dx == -1)
489       dx = This->lpOutFormat->biWidth;
490     if (dy == -1)
491       dy = This->lpOutFormat->biHeight;
492
493     /* need to resize? */
494     if (x != 0 || y != 0) {
495       if (dy == This->lpOutFormat->biHeight &&
496           dx == This->lpOutFormat->biWidth)
497         This->bResize = FALSE;
498       else
499         This->bResize = TRUE;
500     }
501
502     if (This->bResize) {
503       This->x  = x;
504       This->y  = y;
505       This->dx = dx;
506       This->dy = dy;
507
508       if (ICDecompressExBegin(This->hic,0,This->lpInFormat,This->lpInBuffer,0,
509                               0,This->lpInFormat->biWidth,
510                               This->lpInFormat->biHeight,This->lpOutFormat,
511                               This->lpOutBuffer, x, y, dx, dy) == ICERR_OK)
512         return AVIERR_OK;
513     } else if (ICDecompressBegin(This->hic, This->lpInFormat,
514                                  This->lpOutFormat) == ICERR_OK)
515       return AVIERR_OK;
516
517     AVIFILE_CloseCompressor(This);
518
519     return AVIERR_COMPRESSOR;
520   }
521 }
522
523 /***********************************************************************/