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