- First part of a new IAVIEditStream implementation.
[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 "windowsx.h"
27 #include "wingdi.h"
28 #include "winuser.h"
29 #include "vfw.h"
30
31 #include "avifile_private.h"
32
33 #include "wine/debug.h"
34
35 WINE_DEFAULT_DEBUG_CHANNEL(avifile);
36
37 #ifndef DIBPTR
38 #define DIBPTR(lp)      ((LPBYTE)(lp) + (lp)->biSize + \
39                          (lp)->biClrUsed * sizeof(RGBQUAD))
40 #endif
41
42 /***********************************************************************/
43
44 static HRESULT WINAPI IGetFrame_fnQueryInterface(IGetFrame *iface,
45                                                  REFIID refiid, LPVOID *obj);
46 static ULONG   WINAPI IGetFrame_fnAddRef(IGetFrame *iface);
47 static ULONG   WINAPI IGetFrame_fnRelease(IGetFrame *iface);
48 static LPVOID  WINAPI IGetFrame_fnGetFrame(IGetFrame *iface, LONG lPos);
49 static HRESULT WINAPI IGetFrame_fnBegin(IGetFrame *iface, LONG lStart,
50                                         LONG lEnd, LONG lRate);
51 static HRESULT WINAPI IGetFrame_fnEnd(IGetFrame *iface);
52 static HRESULT WINAPI IGetFrame_fnSetFormat(IGetFrame *iface,
53                                             LPBITMAPINFOHEADER lpbi,
54                                             LPVOID lpBits, INT x, INT y,
55                                             INT dx, INT dy);
56
57 struct ICOM_VTABLE(IGetFrame) igetframeVtbl = {
58   ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
59   IGetFrame_fnQueryInterface,
60   IGetFrame_fnAddRef,
61   IGetFrame_fnRelease,
62   IGetFrame_fnGetFrame,
63   IGetFrame_fnBegin,
64   IGetFrame_fnEnd,
65   IGetFrame_fnSetFormat
66 };
67
68 typedef struct _IGetFrameImpl {
69   /* IUnknown stuff */
70   ICOM_VFIELD(IGetFrame);
71   DWORD              ref;
72
73   /* IGetFrame stuff */
74   BOOL               bFixedStream;
75   PAVISTREAM         pStream;
76
77   LPVOID             lpInBuffer;
78   LONG               cbInBuffer;
79   LPBITMAPINFOHEADER lpInFormat;
80   LONG               cbInFormat;
81
82   LONG               lCurrentFrame;
83   LPBITMAPINFOHEADER lpOutFormat;
84   LPVOID             lpOutBuffer;
85
86   HIC                hic;
87   BOOL               bResize;
88   DWORD              x;
89   DWORD              y;
90   DWORD              dx;
91   DWORD              dy;
92
93   BOOL               bFormatChanges;
94   DWORD              dwFormatChangeCount;
95   DWORD              dwEditCount;
96 } IGetFrameImpl;
97
98 /***********************************************************************/
99
100 static void AVIFILE_CloseCompressor(IGetFrameImpl *This)
101 {
102   if (This->lpOutFormat != NULL && This->lpInFormat != This->lpOutFormat) {
103     GlobalFreePtr(This->lpOutFormat);
104     This->lpOutFormat = NULL;
105   }
106   if (This->lpInFormat != NULL) {
107     GlobalFreePtr(This->lpInFormat);
108     This->lpInFormat = NULL;
109   }
110   if (This->hic != NULL) {
111     if (This->bResize)
112       ICDecompressExEnd(This->hic);
113     else
114       ICDecompressEnd(This->hic);
115     ICClose(This->hic);
116     This->hic = NULL;
117   }
118 }
119
120 PGETFRAME AVIFILE_CreateGetFrame(PAVISTREAM pStream)
121 {
122   IGetFrameImpl *pg;
123
124   /* check parameter */
125   if (pStream == NULL)
126     return NULL;
127
128   pg = (IGetFrameImpl*)LocalAlloc(LPTR, sizeof(IGetFrameImpl));
129   if (pg != NULL) {
130     pg->lpVtbl        = &igetframeVtbl;
131     pg->ref           = 1;
132     pg->lCurrentFrame = -1;
133     pg->pStream       = pStream;
134     IAVIStream_AddRef(pStream);
135   }
136
137   return (PGETFRAME)pg;
138 }
139
140 static HRESULT WINAPI IGetFrame_fnQueryInterface(IGetFrame *iface,
141                                                  REFIID refiid, LPVOID *obj)
142 {
143   ICOM_THIS(IGetFrameImpl,iface);
144
145   TRACE("(%p,%s,%p)\n", This, debugstr_guid(refiid), obj);
146
147   if (IsEqualGUID(&IID_IUnknown, refiid) ||
148       IsEqualGUID(&IID_IGetFrame, refiid)) {
149     *obj = iface;
150     return S_OK;
151   }
152
153   return OLE_E_ENUM_NOMORE;
154 }
155
156 static ULONG   WINAPI IGetFrame_fnAddRef(IGetFrame *iface)
157 {
158   ICOM_THIS(IGetFrameImpl,iface);
159
160   TRACE("(%p)\n", iface);
161
162   return ++(This->ref);
163 }
164
165 static ULONG   WINAPI IGetFrame_fnRelease(IGetFrame *iface)
166 {
167   ICOM_THIS(IGetFrameImpl,iface);
168
169   TRACE("(%p)\n", iface);
170
171   if (!--(This->ref)) {
172     AVIFILE_CloseCompressor(This);
173     if (This->pStream != NULL) {
174       IAVIStream_Release(This->pStream);
175       This->pStream = NULL;
176     }
177
178     LocalFree((HLOCAL)iface);
179     return 0;
180   }
181
182   return This->ref;
183 }
184
185 static LPVOID  WINAPI IGetFrame_fnGetFrame(IGetFrame *iface, LONG lPos)
186 {
187   ICOM_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 = GlobalReAllocPtr(This->lpInFormat, This->cbInBuffer, 0);
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   ICOM_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   ICOM_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   ICOM_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 ((LONG)lpbiWanted == 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 =
370       (LPBITMAPINFOHEADER)GlobalAllocPtr(GHND, This->cbInFormat + This->cbInBuffer);
371     if (This->lpInFormat == NULL) {
372       AVIFILE_CloseCompressor(This);
373       return AVIERR_MEMORY;
374     }
375
376     hr = IAVIStream_ReadFormat(This->pStream, sInfo.dwStart, This->lpInFormat, &This->cbInFormat);
377     if (FAILED(hr)) {
378       AVIFILE_CloseCompressor(This);
379       return hr;
380     }
381
382     This->lpInBuffer = ((LPBYTE)This->lpInFormat) + This->cbInFormat;
383   }
384
385   /* check input format */
386   if (This->lpInFormat->biClrUsed == 0 && This->lpInFormat->biBitCount <= 8)
387     This->lpInFormat->biClrUsed = 1u << This->lpInFormat->biBitCount;
388   if (This->lpInFormat->biSizeImage == 0 &&
389       This->lpInFormat->biCompression == BI_RGB) {
390     This->lpInFormat->biSizeImage =
391       DIBWIDTHBYTES(*This->lpInFormat) * This->lpInFormat->biHeight;
392   }
393
394   /* only to pass through? */
395   if (This->lpInFormat->biCompression == BI_RGB && lpBits == NULL) {
396     if (lpbi == NULL || 
397         (lpbi->biCompression == BI_RGB &&
398          lpbi->biWidth == This->lpInFormat->biWidth &&
399          lpbi->biHeight == This->lpInFormat->biHeight &&
400          lpbi->biBitCount == This->lpInFormat->biBitCount)) {
401       This->lpOutFormat = This->lpInFormat;
402       This->lpOutBuffer = DIBPTR(This->lpInFormat);
403       return AVIERR_OK;
404     }
405   }
406
407   /* need memory for output format? */
408   if (This->lpOutFormat == NULL) {
409     This->lpOutFormat =
410       (LPBITMAPINFOHEADER)GlobalAllocPtr(GHND, sizeof(BITMAPINFOHEADER)
411                                          + 256 * sizeof(RGBQUAD));
412     if (This->lpOutFormat == NULL) {
413       AVIFILE_CloseCompressor(This);
414       return AVIERR_MEMORY;
415     }
416   }
417
418   /* need handle to video compressor */
419   if (This->hic == NULL) {
420     FOURCC fccHandler;
421
422     if (This->lpInFormat->biCompression == BI_RGB)
423       fccHandler = comptypeDIB;
424     else if (This->lpInFormat->biCompression == BI_RLE8)
425       fccHandler = mmioFOURCC('R','L','E',' ');
426     else
427       fccHandler = sInfo.fccHandler;
428
429     if (lpbi != NULL) {
430       if (lpbi->biWidth == 0)
431         lpbi->biWidth = This->lpInFormat->biWidth;
432       if (lpbi->biHeight == 0)
433         lpbi->biHeight = This->lpInFormat->biHeight;
434     }
435
436     This->hic = ICLocate(ICTYPE_VIDEO, fccHandler, This->lpInFormat, lpbi, ICMODE_DECOMPRESS);
437     if (This->hic == NULL) {
438       AVIFILE_CloseCompressor(This);
439       return AVIERR_NOCOMPRESSOR;
440     }
441   }
442
443   /* output format given? */
444   if (lpbi != NULL) {
445     /* check the given output format ... */
446     if (lpbi->biClrUsed == 0 && lpbi->biBitCount <= 8)
447       lpbi->biClrUsed = 1u << lpbi->biBitCount;
448
449     /* ... and remember it */
450     memcpy(This->lpOutFormat, lpbi,
451            lpbi->biSize + lpbi->biClrUsed * sizeof(RGBQUAD));
452     if (lpbi->biBitCount <= 8)
453       ICDecompressGetPalette(This->hic, This->lpInFormat, This->lpOutFormat);
454
455     return AVIERR_OK;
456   } else {
457     if (bBestDisplay) {
458       ICGetDisplayFormat(This->hic, This->lpInFormat,
459                          This->lpOutFormat, 0, dx, dy);
460     } else if (ICDecompressGetFormat(This->hic, This->lpInFormat,
461                                      This->lpOutFormat) < 0) {
462       AVIFILE_CloseCompressor(This);
463       return AVIERR_NOCOMPRESSOR;
464     }
465
466     /* check output format */
467     if (This->lpOutFormat->biClrUsed == 0 &&
468         This->lpOutFormat->biBitCount <= 8)
469       This->lpOutFormat->biClrUsed = 1u << This->lpOutFormat->biBitCount;
470     if (This->lpOutFormat->biSizeImage == 0 &&
471         This->lpOutFormat->biCompression == BI_RGB) {
472       This->lpOutFormat->biSizeImage =
473         DIBWIDTHBYTES(*This->lpOutFormat) * This->lpOutFormat->biHeight;
474     }
475
476     if (lpBits == NULL) {
477       register DWORD size = This->lpOutFormat->biClrUsed * sizeof(RGBQUAD);
478
479       size += This->lpOutFormat->biSize + This->lpOutFormat->biSizeImage;
480       This->lpOutFormat =
481         (LPBITMAPINFOHEADER)GlobalReAllocPtr(This->lpOutFormat, size, GMEM_MOVEABLE);
482       if (This->lpOutFormat == NULL) {
483         AVIFILE_CloseCompressor(This);
484         return AVIERR_MEMORY;
485       }
486       This->lpOutBuffer = DIBPTR(This->lpOutFormat);
487     } else
488       This->lpOutBuffer = lpBits;
489
490     /* for user size was irrelevant */
491     if (dx == -1)
492       dx = This->lpOutFormat->biWidth;
493     if (dy == -1)
494       dy = This->lpOutFormat->biHeight;
495
496     /* need to resize? */
497     if (x != 0 || y != 0) {
498       if (dy == This->lpOutFormat->biHeight &&
499           dx == This->lpOutFormat->biWidth)
500         This->bResize = FALSE;
501       else
502         This->bResize = TRUE;
503     }
504
505     if (This->bResize) {
506       This->x  = x;
507       This->y  = y;
508       This->dx = dx;
509       This->dy = dy;
510
511       if (ICDecompressExBegin(This->hic,0,This->lpInFormat,This->lpInBuffer,0,
512                               0,This->lpInFormat->biWidth,
513                               This->lpInFormat->biHeight,This->lpOutFormat,
514                               This->lpOutBuffer, x, y, dx, dy) == ICERR_OK)
515         return AVIERR_OK;
516     } else if (ICDecompressBegin(This->hic, This->lpInFormat,
517                                  This->lpOutFormat) == ICERR_OK)
518       return AVIERR_OK;
519
520     AVIFILE_CloseCompressor(This);
521
522     return AVIERR_COMPRESSOR;
523   }
524 }
525
526 /***********************************************************************/