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