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