Added a framework for testing CreateProcess and a few tests.
[wine] / dlls / avifil32 / igframe.c
1 /*
2  * Copyright 2001 Hidenori TAKESHIMA <hidenori@a2.ctktv.ne.jp>
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  * FIXME - implements color space(depth) converter.
19  */
20
21 #include <string.h>
22 #include <stdio.h>
23 #include <assert.h>
24
25 #include "winbase.h"
26 #include "winnls.h"
27 #include "mmsystem.h"
28 #include "winerror.h"
29 #include "vfw.h"
30 #include "wine/debug.h"
31 #include "avifile_private.h"
32
33 WINE_DEFAULT_DEBUG_CHANNEL(avifile);
34
35 static HRESULT WINAPI IGetFrame_fnQueryInterface(IGetFrame* iface,REFIID refiid,LPVOID *obj);
36 static ULONG WINAPI IGetFrame_fnAddRef(IGetFrame* iface);
37 static ULONG WINAPI IGetFrame_fnRelease(IGetFrame* iface);
38 static LPVOID WINAPI IGetFrame_fnGetFrame(IGetFrame* iface,LONG lPos);
39 static HRESULT WINAPI IGetFrame_fnBegin(IGetFrame* iface,LONG lStart,LONG lEnd,LONG lRate);
40 static HRESULT WINAPI IGetFrame_fnEnd(IGetFrame* iface);
41 static HRESULT WINAPI IGetFrame_fnSetFormat(IGetFrame* iface,LPBITMAPINFOHEADER lpbi,LPVOID lpBits,INT x,INT y,INT dx,INT dy);
42
43 struct ICOM_VTABLE(IGetFrame) igetfrm = {
44     ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
45         IGetFrame_fnQueryInterface,
46         IGetFrame_fnAddRef,
47         IGetFrame_fnRelease,
48         IGetFrame_fnGetFrame,
49         IGetFrame_fnBegin,
50         IGetFrame_fnEnd,
51         IGetFrame_fnSetFormat,
52 };
53
54 typedef struct IGetFrameImpl
55 {
56         ICOM_VFIELD(IGetFrame);
57         /* IUnknown stuff */
58         DWORD                   ref;
59         /* IGetFrame stuff */
60         IAVIStream*             pas;
61         HIC                     hIC;
62         LONG                    lCachedFrame;
63         BITMAPINFO*             pbiICIn;
64         BITMAPINFO*             pbiICOut;
65         LPVOID                  pvICOutBits;
66         LPVOID                  pvICInFmtBuf;
67         DWORD                   dwICInDataBufSize;
68         LPVOID                  pvICInDataBuf;
69         LPVOID                  pvICOutBuf;
70 } IGetFrameImpl;
71
72 static HRESULT IGetFrame_Construct( IGetFrameImpl* This,
73                                     IAVIStream* pstr,
74                                     LPBITMAPINFOHEADER lpbi );
75 static void IGetFrame_Destruct( IGetFrameImpl* This );
76
77
78
79
80 static LPVOID AVIFILE_IGetFrame_DecodeFrame(IGetFrameImpl* This,LONG lPos)
81 {
82         HRESULT hr;
83         DWORD   dwRes;
84         LONG    lFrameLength;
85         LONG    lSampleCount;
86         ICDECOMPRESS    icd;
87
88         if ( This->hIC == (HIC)NULL )
89                 return NULL;
90
91         hr = IAVIStream_Read(This->pas,lPos,1,NULL,0,
92                              &lFrameLength,&lSampleCount);
93         if ( hr != S_OK || lSampleCount <= 0 )
94         {
95                 FIXME( "IAVIStream_Read failed! res = %08lx\n", hr );
96                 return NULL;
97         }
98         TRACE( "frame length = %ld\n", lFrameLength );
99
100         if ( This->dwICInDataBufSize < lFrameLength )
101         {
102                 LPVOID  lpv;
103
104                 if ( This->pvICInDataBuf == NULL )
105                 {
106                         lpv = HeapAlloc(
107                                 AVIFILE_data.hHeap,HEAP_ZERO_MEMORY,
108                                 lFrameLength );
109                 }
110                 else
111                 {
112                         lpv = HeapReAlloc(
113                                 AVIFILE_data.hHeap,HEAP_ZERO_MEMORY,
114                                 This->pvICInDataBuf,lFrameLength );
115                 }
116                 if ( lpv == NULL )
117                 {
118                         ERR( "out of memory!\n" );
119                         return NULL;
120                 }
121                 This->pvICInDataBuf = lpv;
122                 This->dwICInDataBufSize = lFrameLength;
123         }
124
125         hr = IAVIStream_Read(This->pas,lPos,1,
126                              This->pvICInDataBuf,This->dwICInDataBufSize,
127                              &lFrameLength,&lSampleCount);
128         if ( hr != S_OK || lSampleCount <= 0 )
129         {
130                 FIXME( "IAVIStream_Read to buffer failed! res = %08lx\n", hr );
131                 return NULL;
132         }
133
134         This->pbiICIn->bmiHeader.biSizeImage = lFrameLength;
135
136         TRACE( "call ICM_DECOMPRESS\n" );
137         icd.dwFlags = 0;
138         if ( IAVIStream_FindSample(This->pas,lPos,FIND_PREV|FIND_KEY) != lPos )
139                 icd.dwFlags = ICDECOMPRESS_NOTKEYFRAME;
140         icd.lpbiInput = &This->pbiICIn->bmiHeader;
141         icd.lpInput = (BYTE*)This->pvICInDataBuf;
142         icd.lpbiOutput = &This->pbiICOut->bmiHeader;
143         icd.lpOutput = This->pvICOutBits;
144         icd.ckid = *((DWORD*)This->pvICInDataBuf);
145         dwRes = ICSendMessage(This->hIC,ICM_DECOMPRESS,
146                               (DWORD)(&icd),sizeof(ICDECOMPRESS) );
147         TRACE( "returned from ICM_DECOMPRESS\n" );
148         if ( dwRes != ICERR_OK )
149         {
150                 ERR( "ICDecompress failed!\n" );
151                 return NULL;
152         }
153
154         This->lCachedFrame = lPos;
155
156         return This->pvICOutBits;
157 }
158
159 /****************************************************************************/
160
161 HRESULT AVIFILE_CreateIGetFrame(void** ppobj,
162                                 IAVIStream* pstr,LPBITMAPINFOHEADER lpbi)
163 {
164         IGetFrameImpl   *This;
165         HRESULT         hr;
166
167         *ppobj = NULL;
168         This = (IGetFrameImpl*)HeapAlloc(AVIFILE_data.hHeap,HEAP_ZERO_MEMORY,
169                                           sizeof(IGetFrameImpl));
170         This->ref = 1;
171         ICOM_VTBL(This) = &igetfrm;
172         hr = IGetFrame_Construct( This, pstr, lpbi );
173         if ( hr != S_OK )
174         {
175                 IGetFrame_Destruct( This );
176                 return hr;
177         }
178
179         *ppobj = (LPVOID)This;
180
181         return S_OK;
182 }
183
184 /****************************************************************************
185  * IUnknown interface
186  */
187
188 static HRESULT WINAPI IGetFrame_fnQueryInterface(IGetFrame* iface,REFIID refiid,LPVOID *obj)
189 {
190         ICOM_THIS(IGetFrameImpl,iface);
191
192         TRACE("(%p)->QueryInterface(%s,%p)\n",This,debugstr_guid(refiid),obj);
193         if ( IsEqualGUID(&IID_IUnknown,refiid) ||
194              IsEqualGUID(&IID_IGetFrame,refiid) )
195         {
196                 IGetFrame_AddRef(iface);
197                 *obj = iface;
198                 return S_OK;
199         }
200
201         return OLE_E_ENUM_NOMORE;
202 }
203
204 static ULONG WINAPI IGetFrame_fnAddRef(IGetFrame* iface)
205 {
206         ICOM_THIS(IGetFrameImpl,iface);
207
208         TRACE("(%p)->AddRef()\n",iface);
209         return ++(This->ref);
210 }
211
212 static ULONG WINAPI IGetFrame_fnRelease(IGetFrame* iface)
213 {
214         ICOM_THIS(IGetFrameImpl,iface);
215
216         TRACE("(%p)->Release()\n",iface);
217         if ((--(This->ref)) > 0 )
218                 return This->ref;
219         IGetFrame_Destruct(This);
220         if ( This->pas != NULL )
221                 IAVIStream_Release( This->pas );
222
223         HeapFree(AVIFILE_data.hHeap,0,iface);
224         return 0;
225 }
226
227 /****************************************************************************
228  * IGetFrrame interface
229  */
230
231 static LPVOID WINAPI IGetFrame_fnGetFrame(IGetFrame* iface,LONG lPos)
232 {
233         ICOM_THIS(IGetFrameImpl,iface);
234         LPVOID  lpv;
235         LONG    lKeyFrame;
236
237         TRACE( "(%p)->(%ld)\n", This, lPos );
238
239         if ( lPos < 0 )
240                 return NULL;
241
242         if ( This->lCachedFrame == lPos )
243                 return This->pvICOutBits;
244         if ( (This->lCachedFrame+1) != lPos )
245         {
246                 lKeyFrame = IAVIStream_FindSample( This->pas, lPos,
247                                                    FIND_KEY | FIND_PREV );
248                 if ( lKeyFrame < 0 || lKeyFrame > lPos )
249                         return NULL;
250                 while ( ++lKeyFrame < lPos )
251                 {
252                         lpv = AVIFILE_IGetFrame_DecodeFrame(This, lKeyFrame);
253                         if ( lpv == NULL )
254                                 return NULL;
255                 }
256         }
257
258         lpv = AVIFILE_IGetFrame_DecodeFrame(This, lPos);
259         TRACE( "lpv = %p\n",lpv );
260         if ( lpv == NULL )
261                 return NULL;
262
263         return lpv;
264 }
265
266 static HRESULT WINAPI IGetFrame_fnBegin(IGetFrame* iface,LONG lStart,LONG lEnd,LONG lRate)
267 {
268         ICOM_THIS(IGetFrameImpl,iface);
269
270         TRACE( "(%p)->(%ld,%ld,%ld)\n", This, lStart, lEnd, lRate );
271
272         if ( This->hIC == (HIC)NULL )
273                 return E_UNEXPECTED;
274
275         if ( ICDecompressBegin( This->hIC,
276                                 This->pbiICIn,
277                                 This->pbiICOut ) != ICERR_OK )
278                 return E_FAIL;
279
280         return S_OK;
281 }
282
283 static HRESULT WINAPI IGetFrame_fnEnd(IGetFrame* iface)
284 {
285         ICOM_THIS(IGetFrameImpl,iface);
286
287         TRACE( "(%p)->()\n", This );
288
289         if ( This->hIC == (HIC)NULL )
290                 return E_UNEXPECTED;
291
292         if ( ICDecompressEnd( This->hIC ) != ICERR_OK )
293                 return E_FAIL;
294
295         return S_OK;
296 }
297
298 static HRESULT WINAPI IGetFrame_fnSetFormat(IGetFrame* iface,LPBITMAPINFOHEADER lpbi,LPVOID lpBits,INT x,INT y,INT dx,INT dy)
299 {
300         ICOM_THIS(IGetFrameImpl,iface);
301         HRESULT hr;
302         LONG    fmtlen;
303         BITMAPINFOHEADER        biTemp;
304         DWORD   dwSizeImage;
305
306         FIXME( "(%p)->(%p,%p,%d,%d,%d,%d)\n",This,lpbi,lpBits,x,y,dx,dy );
307
308         IGetFrame_Destruct(This);
309
310         hr = IAVIStream_ReadFormat(This->pas,0,NULL,&fmtlen);
311         if ( hr != S_OK )
312                 return hr;
313         This->pvICInFmtBuf = HeapAlloc(
314                 AVIFILE_data.hHeap,HEAP_ZERO_MEMORY,fmtlen);
315         if ( This->pvICInFmtBuf == NULL )
316                 return AVIERR_MEMORY;
317         hr = IAVIStream_ReadFormat(This->pas,0,This->pvICInFmtBuf,&fmtlen);
318         if ( hr != S_OK )
319                 return hr;
320         This->pbiICIn = (LPBITMAPINFO)This->pvICInFmtBuf;
321
322         This->hIC = (HIC)ICOpen( ICTYPE_VIDEO,
323                                  This->pbiICIn->bmiHeader.biCompression,
324                                  ICMODE_DECOMPRESS );
325         if ( This->hIC == (HIC)NULL )
326         {
327                 ERR( "no AVI decompressor for %c%c%c%c.\n",
328                      (int)(This->pbiICIn->bmiHeader.biCompression>> 0)&0xff,
329                      (int)(This->pbiICIn->bmiHeader.biCompression>> 8)&0xff,
330                      (int)(This->pbiICIn->bmiHeader.biCompression>>16)&0xff,
331                      (int)(This->pbiICIn->bmiHeader.biCompression>>24)&0xff );
332                 return E_FAIL;
333         }
334
335         if ( lpbi == NULL || lpbi == ((LPBITMAPINFOHEADER)1) )
336         {
337                 memset( &biTemp, 0, sizeof(biTemp) );
338                 biTemp.biSize = sizeof(BITMAPINFOHEADER);
339                 biTemp.biWidth = This->pbiICIn->bmiHeader.biWidth;
340                 biTemp.biHeight = This->pbiICIn->bmiHeader.biHeight;
341                 biTemp.biPlanes = 1;
342                 biTemp.biBitCount = 24;
343                 biTemp.biCompression = 0;
344                 lpbi = &biTemp;
345         }
346
347         if ( lpbi->biPlanes != 1 || lpbi->biCompression != 0 )
348                 return E_FAIL;
349
350         dwSizeImage =
351                 ((This->pbiICIn->bmiHeader.biWidth*lpbi->biBitCount+7)/8)*
352                                         This->pbiICIn->bmiHeader.biHeight;
353         This->pvICOutBuf = HeapAlloc(
354                 AVIFILE_data.hHeap,HEAP_ZERO_MEMORY,
355                 (sizeof(BITMAPINFO)+sizeof(RGBQUAD)*256)*2+
356                 dwSizeImage );
357         if ( This->pvICOutBuf == NULL )
358                 return AVIERR_MEMORY;
359
360         This->pbiICOut = (BITMAPINFO*)This->pvICOutBuf;
361         This->pvICOutBits = (LPVOID)( (BYTE*)This->pvICOutBuf +
362                                 sizeof(BITMAPINFO) + sizeof(RGBQUAD)*256 );
363
364         This->pbiICOut->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
365         This->pbiICOut->bmiHeader.biWidth = This->pbiICIn->bmiHeader.biWidth;
366         This->pbiICOut->bmiHeader.biHeight = This->pbiICIn->bmiHeader.biHeight;
367         This->pbiICOut->bmiHeader.biPlanes = 1;
368         This->pbiICOut->bmiHeader.biBitCount = lpbi->biBitCount;
369         This->pbiICOut->bmiHeader.biSizeImage = dwSizeImage;
370         memcpy( This->pvICOutBits, This->pbiICOut, sizeof(BITMAPINFOHEADER) );
371
372         return S_OK;
373 }
374
375 static HRESULT IGetFrame_Construct( IGetFrameImpl* This,
376                                     IAVIStream* pstr,
377                                     LPBITMAPINFOHEADER lpbi )
378 {
379         HRESULT hr;
380
381         TRACE( "(%p)->(%p,%p)\n",This,pstr,lpbi );
382
383         IAVIStream_AddRef( pstr );
384         This->pas = pstr;
385         This->hIC = (HIC)NULL;
386         This->lCachedFrame = -1L;
387         This->pbiICIn = NULL;
388         This->pbiICOut = NULL;
389         This->pvICInFmtBuf = NULL;
390         This->pvICInDataBuf = NULL;
391         This->dwICInDataBufSize = 0;
392         This->pvICOutBuf = NULL;
393
394         hr = IGetFrame_SetFormat((IGetFrame*)This,lpbi,NULL,0,0,0,0);
395         if ( hr != S_OK )
396                 return hr;
397
398         return S_OK;
399 }
400
401 static void IGetFrame_Destruct( IGetFrameImpl* This )
402 {
403         if ( This->hIC != (HIC)NULL )
404         {
405                 ICClose( This->hIC );
406                 This->hIC = (HIC)NULL;
407         }
408         if ( This->pvICInFmtBuf != NULL )
409         {
410                 HeapFree( AVIFILE_data.hHeap, 0, This->pvICInFmtBuf );
411                 This->pvICInFmtBuf = NULL;
412         }
413         if ( This->pvICInDataBuf != NULL )
414         {
415                 HeapFree( AVIFILE_data.hHeap, 0, This->pvICInDataBuf );
416                 This->pvICInDataBuf = NULL;
417         }
418         if ( This->pvICOutBuf != NULL )
419         {
420                 HeapFree( AVIFILE_data.hHeap, 0, This->pvICOutBuf );
421                 This->pvICOutBuf = NULL;
422         }
423
424         This->lCachedFrame = -1L;
425         This->pbiICIn = NULL;
426         This->pbiICOut = NULL;
427         This->dwICInDataBufSize = 0;
428 }