fdopen: don't rewind the file after creating the FILE* handle. Added
[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 != NULL) {
106     if (This->bResize)
107       ICDecompressExEnd(This->hic);
108     else
109       ICDecompressEnd(This->hic);
110     ICClose(This->hic);
111     This->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       return NULL;
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 == 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 == 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     HRESULT hr;
342
343     This->cbInBuffer = sInfo.dwSuggestedBufferSize;
344     if (This->cbInBuffer == 0)
345       This->cbInBuffer = 1024;
346
347     AVIStreamFormatSize(This->pStream, sInfo.dwStart, &This->cbInFormat);
348
349     This->lpInFormat =
350       (LPBITMAPINFOHEADER)GlobalAllocPtr(GHND, This->cbInFormat + This->cbInBuffer);
351     if (This->lpInFormat == NULL) {
352       AVIFILE_CloseCompressor(This);
353       return AVIERR_MEMORY;
354     }
355
356     hr = AVIStreamReadFormat(This->pStream, sInfo.dwStart, This->lpInFormat, &This->cbInFormat);
357     if (FAILED(hr)) {
358       AVIFILE_CloseCompressor(This);
359       return hr;
360     }
361
362     This->lpInBuffer = ((LPBYTE)This->lpInFormat) + This->cbInFormat;
363   }
364
365   /* check input format */
366   if (This->lpInFormat->biClrUsed == 0 && This->lpInFormat->biBitCount <= 8)
367     This->lpInFormat->biClrUsed = 1u << This->lpInFormat->biBitCount;
368   if (This->lpInFormat->biSizeImage == 0 &&
369       This->lpInFormat->biCompression == BI_RGB) {
370     This->lpInFormat->biSizeImage =
371       DIBWIDTHBYTES(*This->lpInFormat) * This->lpInFormat->biHeight;
372   }
373
374   /* only to pass through? */
375   if (This->lpInFormat->biCompression == BI_RGB && lpBits == NULL) {
376     if (lpbi == NULL || 
377         (lpbi->biCompression == BI_RGB &&
378          lpbi->biWidth == This->lpInFormat->biWidth &&
379          lpbi->biHeight == This->lpInFormat->biHeight &&
380          lpbi->biBitCount == This->lpInFormat->biBitCount)) {
381       This->lpOutFormat = This->lpInFormat;
382       This->lpOutBuffer = DIBPTR(This->lpInFormat);
383       return AVIERR_OK;
384     }
385   }
386
387   /* need memory for output format? */
388   if (This->lpOutFormat == NULL) {
389     This->lpOutFormat =
390       (LPBITMAPINFOHEADER)GlobalAllocPtr(GHND, sizeof(BITMAPINFOHEADER)
391                                          + 256 * sizeof(RGBQUAD));
392     if (This->lpOutFormat == NULL) {
393       AVIFILE_CloseCompressor(This);
394       return AVIERR_MEMORY;
395     }
396   }
397
398   /* need handle to video compressor */
399   if (This->hic == NULL) {
400     FOURCC fccHandler;
401
402     if (This->lpInFormat->biCompression == BI_RGB)
403       fccHandler = comptypeDIB;
404     else if (This->lpInFormat->biCompression == BI_RLE8)
405       fccHandler = mmioFOURCC('R','L','E',' ');
406     else
407       fccHandler = sInfo.fccHandler;
408
409     if (lpbi != NULL) {
410       if (lpbi->biWidth == 0)
411         lpbi->biWidth = This->lpInFormat->biWidth;
412       if (lpbi->biHeight == 0)
413         lpbi->biHeight = This->lpInFormat->biHeight;
414     }
415
416     This->hic = ICLocate(ICTYPE_VIDEO, fccHandler, This->lpInFormat, lpbi, ICMODE_DECOMPRESS);
417     if (This->hic == NULL) {
418       AVIFILE_CloseCompressor(This);
419       return AVIERR_NOCOMPRESSOR;
420     }
421   }
422
423   /* output format given? */
424   if (lpbi != NULL) {
425     /* check the given output format ... */
426     if (lpbi->biClrUsed == 0 && lpbi->biBitCount <= 8)
427       lpbi->biClrUsed = 1u << lpbi->biBitCount;
428
429     /* ... and remember it */
430     memcpy(This->lpOutFormat, lpbi,
431            lpbi->biSize + lpbi->biClrUsed * sizeof(RGBQUAD));
432     if (lpbi->biBitCount <= 8)
433       ICDecompressGetPalette(This->hic, This->lpInFormat, This->lpOutFormat);
434
435     return AVIERR_OK;
436   } else {
437     if (bBestDisplay) {
438       ICGetDisplayFormat(This->hic, This->lpInFormat,
439                          This->lpOutFormat, 0, dx, dy);
440     } else if (ICDecompressGetFormat(This->hic, This->lpInFormat,
441                                      This->lpOutFormat) < 0) {
442       AVIFILE_CloseCompressor(This);
443       return AVIERR_NOCOMPRESSOR;
444     }
445
446     /* check output format */
447     if (This->lpOutFormat->biClrUsed == 0 &&
448         This->lpOutFormat->biBitCount <= 8)
449       This->lpOutFormat->biClrUsed = 1u << This->lpOutFormat->biBitCount;
450     if (This->lpOutFormat->biSizeImage == 0 &&
451         This->lpOutFormat->biCompression == BI_RGB) {
452       This->lpOutFormat->biSizeImage =
453         DIBWIDTHBYTES(*This->lpOutFormat) * This->lpOutFormat->biHeight;
454     }
455
456     if (lpBits == NULL) {
457       register DWORD size = This->lpOutFormat->biClrUsed * sizeof(RGBQUAD);
458
459       size += This->lpOutFormat->biSize + This->lpOutFormat->biSizeImage;
460       This->lpOutFormat =
461         (LPBITMAPINFOHEADER)GlobalReAllocPtr(This->lpOutFormat, size, GMEM_MOVEABLE);
462       if (This->lpOutFormat == NULL) {
463         AVIFILE_CloseCompressor(This);
464         return AVIERR_MEMORY;
465       }
466       This->lpOutBuffer = DIBPTR(This->lpOutFormat);
467     } else
468       This->lpOutBuffer = lpBits;
469
470     /* for user size was irrelevant */
471     if (dx == -1)
472       dx = This->lpOutFormat->biWidth;
473     if (dy == -1)
474       dy = This->lpOutFormat->biHeight;
475
476     /* need to resize? */
477     if (x != 0 || y != 0) {
478       if (dy == This->lpOutFormat->biHeight &&
479           dx == This->lpOutFormat->biWidth)
480         This->bResize = FALSE;
481       else
482         This->bResize = TRUE;
483     }
484
485     if (This->bResize) {
486       This->x  = x;
487       This->y  = y;
488       This->dx = dx;
489       This->dy = dy;
490
491       if (ICDecompressExBegin(This->hic,0,This->lpInFormat,This->lpInBuffer,0,
492                               0,This->lpInFormat->biWidth,
493                               This->lpInFormat->biHeight,This->lpOutFormat,
494                               This->lpOutBuffer, x, y, dx, dy) == ICERR_OK)
495         return AVIERR_OK;
496     } else if (ICDecompressBegin(This->hic, This->lpInFormat,
497                                  This->lpOutFormat) == ICERR_OK)
498       return AVIERR_OK;
499
500     AVIFILE_CloseCompressor(This);
501
502     return AVIERR_COMPRESSOR;
503   }
504 }
505
506 /***********************************************************************/