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