Added SystemParametersInfo unit test.
[wine] / dlls / quartz / memalloc.c
1 /*
2  * Implementation of CLSID_MemoryAllocator.
3  *
4  * hidenori@a2.ctktv.ne.jp
5  */
6
7 #include "config.h"
8
9 #include "windef.h"
10 #include "winbase.h"
11 #include "wingdi.h"
12 #include "winuser.h"
13 #include "winerror.h"
14 #include "strmif.h"
15 #include "uuids.h"
16 #include "vfwmsgs.h"
17
18 #include "debugtools.h"
19 DEFAULT_DEBUG_CHANNEL(quartz);
20
21 #include "quartz_private.h"
22 #include "memalloc.h"
23
24
25 /***************************************************************************
26  *
27  *      new/delete for CLSID_MemoryAllocator.
28  *
29  */
30
31 /* can I use offsetof safely? - FIXME? */
32 static QUARTZ_IFEntry IFEntries[] =
33 {
34   { &IID_IMemAllocator, offsetof(CMemoryAllocator,memalloc)-offsetof(CMemoryAllocator,unk) },
35 };
36
37 static void QUARTZ_DestroyMemoryAllocator(IUnknown* punk)
38 {
39         CMemoryAllocator_THIS(punk,unk);
40
41         CMemoryAllocator_UninitIMemAllocator( This );
42 }
43
44 HRESULT QUARTZ_CreateMemoryAllocator(IUnknown* punkOuter,void** ppobj)
45 {
46         CMemoryAllocator*       pma;
47         HRESULT hr;
48
49         TRACE("(%p,%p)\n",punkOuter,ppobj);
50
51         pma = (CMemoryAllocator*)QUARTZ_AllocObj( sizeof(CMemoryAllocator) );
52         if ( pma == NULL )
53                 return E_OUTOFMEMORY;
54
55         QUARTZ_IUnkInit( &pma->unk, punkOuter );
56         hr = CMemoryAllocator_InitIMemAllocator( pma );
57         if ( FAILED(hr) )
58         {
59                 QUARTZ_FreeObj( pma );
60                 return hr;
61         }
62
63         pma->unk.pEntries = IFEntries;
64         pma->unk.dwEntries = sizeof(IFEntries)/sizeof(IFEntries[0]);
65         pma->unk.pOnFinalRelease = QUARTZ_DestroyMemoryAllocator;
66
67         *ppobj = (void*)(&pma->unk);
68
69         return S_OK;
70 }
71
72
73 /***************************************************************************
74  *
75  *      CMemoryAllocator::IMemAllocator
76  *
77  */
78
79 static HRESULT
80 IMemAllocator_LockUnusedBuffer(CMemoryAllocator* This,IMediaSample** ppSample)
81 {
82         HRESULT hr = E_FAIL;
83         LONG    i;
84
85         TRACE("(%p) try to enter critical section\n",This);
86         EnterCriticalSection( &This->csMem );
87         TRACE("(%p) enter critical section\n",This);
88
89         if ( This->pData == NULL || This->ppSamples == NULL ||
90              This->prop.cBuffers <= 0 )
91         {
92                 hr = VFW_E_NOT_COMMITTED;
93                 goto end;
94         }
95
96
97         for ( i = 0; i < This->prop.cBuffers; i++ )
98         {
99                 if ( This->ppSamples[i] == NULL )
100                 {
101                         hr = VFW_E_NOT_COMMITTED;
102                         goto end;
103                 }
104                 if ( This->ppSamples[i]->ref == 0 )
105                 {
106                         *ppSample = (IMediaSample*)(This->ppSamples[i]);
107                         IMediaSample_AddRef( *ppSample );
108                         hr = NOERROR;
109                         goto end;
110                 }
111         }
112
113         hr = VFW_E_TIMEOUT;
114 end:
115         LeaveCriticalSection( &This->csMem );
116         TRACE("(%p) leave critical section\n",This);
117
118         return hr;
119 }
120
121 /* TRUE = all samples are released */
122 static BOOL
123 IMemAllocator_ReleaseUnusedBuffer(CMemoryAllocator* This)
124 {
125         LONG    i;
126         BOOL    bRet = TRUE;
127
128         TRACE("(%p) try to enter critical section\n",This);
129         EnterCriticalSection( &This->csMem );
130         TRACE("(%p) enter critical section\n",This);
131
132         if ( This->pData == NULL || This->ppSamples == NULL ||
133              This->prop.cBuffers <= 0 )
134                 goto end;
135
136         for ( i = 0; i < This->prop.cBuffers; i++ )
137         {
138                 if ( This->ppSamples[i]->ref == 0 )
139                 {
140                         QUARTZ_DestroyMemMediaSample( This->ppSamples[i] );
141                         This->ppSamples[i] = NULL;
142                 }
143                 else
144                 {
145                         bRet = FALSE;
146                 }
147         }
148
149         if ( bRet )
150         {
151                 QUARTZ_FreeMem(This->ppSamples);
152                 This->ppSamples = NULL;
153                 QUARTZ_FreeMem(This->pData);
154                 This->pData = NULL;
155         }
156
157 end:
158         LeaveCriticalSection( &This->csMem );
159         TRACE("(%p) leave critical section\n",This);
160
161         return bRet;
162 }
163
164
165 static HRESULT WINAPI
166 IMemAllocator_fnQueryInterface(IMemAllocator* iface,REFIID riid,void** ppobj)
167 {
168         CMemoryAllocator_THIS(iface,memalloc);
169
170         TRACE("(%p)->()\n",This);
171
172         return IUnknown_QueryInterface(This->unk.punkControl,riid,ppobj);
173 }
174
175 static ULONG WINAPI
176 IMemAllocator_fnAddRef(IMemAllocator* iface)
177 {
178         CMemoryAllocator_THIS(iface,memalloc);
179
180         TRACE("(%p)->()\n",This);
181
182         return IUnknown_AddRef(This->unk.punkControl);
183 }
184
185 static ULONG WINAPI
186 IMemAllocator_fnRelease(IMemAllocator* iface)
187 {
188         CMemoryAllocator_THIS(iface,memalloc);
189
190         TRACE("(%p)->()\n",This);
191
192         return IUnknown_Release(This->unk.punkControl);
193 }
194
195 static HRESULT WINAPI
196 IMemAllocator_fnSetProperties(IMemAllocator* iface,ALLOCATOR_PROPERTIES* pPropReq,ALLOCATOR_PROPERTIES* pPropActual)
197 {
198         CMemoryAllocator_THIS(iface,memalloc);
199         long    padding;
200         HRESULT hr;
201
202         TRACE( "(%p)->(%p,%p)\n", This, pPropReq, pPropActual );
203
204         if ( pPropReq == NULL || pPropActual == NULL )
205                 return E_POINTER;
206         if ( pPropReq->cBuffers < 0 ||
207              pPropReq->cbBuffer < 0 ||
208              pPropReq->cbAlign < 0 ||
209              pPropReq->cbPrefix < 0 )
210         {
211                 TRACE("pPropReq is invalid\n");
212                 return E_INVALIDARG;
213         }
214
215         if ( pPropReq->cbAlign == 0 ||
216              ( pPropReq->cbAlign & (pPropReq->cbAlign-1) ) != 0 )
217         {
218                 WARN("cbAlign is invalid - %ld\n",pPropReq->cbAlign);
219                 return VFW_E_BADALIGN;
220         }
221
222         hr = NOERROR;
223
224         EnterCriticalSection( &This->csMem );
225
226         if ( This->pData != NULL || This->ppSamples != NULL )
227         {
228                 /* if commited, properties must not be changed. */
229                 TRACE("already commited\n");
230                 hr = E_UNEXPECTED;
231                 goto end;
232         }
233
234         This->prop.cBuffers = pPropReq->cBuffers;
235         This->prop.cbBuffer = pPropReq->cbBuffer;
236         This->prop.cbAlign = pPropReq->cbAlign;
237         This->prop.cbPrefix = pPropReq->cbPrefix;
238
239         if ( This->prop.cbAlign == 0 )
240                 This->prop.cbAlign = 1;
241         padding = This->prop.cbAlign -
242                 ( (This->prop.cbBuffer+This->prop.cbPrefix) % This->prop.cbAlign );
243
244         This->prop.cbBuffer += padding;
245
246         memcpy( pPropActual, &This->prop, sizeof(ALLOCATOR_PROPERTIES) );
247
248 end:
249         LeaveCriticalSection( &This->csMem );
250
251         TRACE("returned successfully.\n");
252
253         return hr;
254 }
255
256 static HRESULT WINAPI
257 IMemAllocator_fnGetProperties(IMemAllocator* iface,ALLOCATOR_PROPERTIES* pProp)
258 {
259         CMemoryAllocator_THIS(iface,memalloc);
260
261         TRACE( "(%p)->(%p)\n", This, pProp );
262
263         if ( pProp == NULL )
264                 return E_POINTER;
265
266         EnterCriticalSection( &This->csMem );
267
268         memcpy( pProp, &This->prop, sizeof(ALLOCATOR_PROPERTIES) );
269
270         LeaveCriticalSection( &This->csMem );
271
272         return NOERROR;
273 }
274
275 static HRESULT WINAPI
276 IMemAllocator_fnCommit(IMemAllocator* iface)
277 {
278         CMemoryAllocator_THIS(iface,memalloc);
279         HRESULT hr;
280         LONG    lBufSize;
281         LONG    i;
282         BYTE*   pCur;
283
284         TRACE( "(%p)->()\n", This );
285
286         EnterCriticalSection( &This->csMem );
287
288         hr = NOERROR;
289         /* FIXME - handle in Decommitting */
290         if ( This->pData != NULL || This->ppSamples != NULL ||
291              This->prop.cBuffers <= 0 )
292                 goto end;
293
294         lBufSize = This->prop.cBuffers *
295                 (This->prop.cbBuffer + This->prop.cbPrefix) +
296                 This->prop.cbAlign;
297         if ( lBufSize <= 0 )
298                 lBufSize = 1;
299
300         This->pData = (BYTE*)QUARTZ_AllocMem( lBufSize );
301         if ( This->pData == NULL )
302         {
303                 hr = E_OUTOFMEMORY;
304                 goto end;
305         }
306
307         This->ppSamples = (CMemMediaSample**)QUARTZ_AllocMem(
308                 sizeof(CMemMediaSample*) * This->prop.cBuffers );
309         if ( This->ppSamples == NULL )
310         {
311                 hr = E_OUTOFMEMORY;
312                 goto end;
313         }
314
315         for ( i = 0; i < This->prop.cBuffers; i++ )
316                 This->ppSamples[i] = NULL;
317
318         pCur = This->pData + This->prop.cbAlign - ((This->pData-(BYTE*)NULL) & (This->prop.cbAlign-1));
319
320         for ( i = 0; i < This->prop.cBuffers; i++ )
321         {
322                 hr = QUARTZ_CreateMemMediaSample(
323                         pCur, (This->prop.cbBuffer + This->prop.cbPrefix),
324                         iface, &This->ppSamples[i] );
325                 if ( FAILED(hr) )
326                         goto end;
327                 pCur += (This->prop.cbBuffer + This->prop.cbPrefix);
328         }
329
330         hr = NOERROR;
331 end:
332         if ( FAILED(hr) )
333                 IMemAllocator_Decommit(iface);
334
335         LeaveCriticalSection( &This->csMem );
336
337         return hr;
338 }
339
340 static HRESULT WINAPI
341 IMemAllocator_fnDecommit(IMemAllocator* iface)
342 {
343         CMemoryAllocator_THIS(iface,memalloc);
344
345         TRACE( "(%p)->()\n", This );
346
347         while ( 1 )
348         {
349                 ResetEvent( This->hEventSample );
350
351                 /* to avoid deadlock, don't hold critical section while blocking */
352                 if ( IMemAllocator_ReleaseUnusedBuffer(This) )
353                         break;
354
355                 WaitForSingleObject( This->hEventSample, INFINITE );
356         }
357
358         return NOERROR;
359 }
360
361 static HRESULT WINAPI
362 IMemAllocator_fnGetBuffer(IMemAllocator* iface,IMediaSample** ppSample,REFERENCE_TIME* prtStart,REFERENCE_TIME* prtEnd,DWORD dwFlags)
363 {
364         CMemoryAllocator_THIS(iface,memalloc);
365         HRESULT hr;
366
367         TRACE( "(%p)->(%p,%p,%p,%lu)\n", This, ppSample, prtStart, prtEnd, dwFlags );
368
369         if ( ppSample == NULL )
370                 return E_POINTER;
371
372         while ( 1 )
373         {
374                 ResetEvent( This->hEventSample );
375
376                 /* to avoid deadlock, don't hold critical section while blocking */
377                 hr = IMemAllocator_LockUnusedBuffer(This,ppSample);
378                 if ( ( hr != VFW_E_TIMEOUT ) || ( dwFlags & AM_GBF_NOWAIT ) )
379                         goto end;
380
381                 WaitForSingleObject( This->hEventSample, INFINITE );
382         }
383
384 end:
385
386         return hr;
387 }
388
389 static HRESULT WINAPI
390 IMemAllocator_fnReleaseBuffer(IMemAllocator* iface,IMediaSample* pSample)
391 {
392         CMemoryAllocator_THIS(iface,memalloc);
393
394         TRACE( "(%p)->(%p)\n", This, pSample );
395         SetEvent( This->hEventSample );
396
397         return NOERROR;
398 }
399
400
401
402 static ICOM_VTABLE(IMemAllocator) imemalloc =
403 {
404         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
405         /* IUnknown fields */
406         IMemAllocator_fnQueryInterface,
407         IMemAllocator_fnAddRef,
408         IMemAllocator_fnRelease,
409         /* IMemAllocator fields */
410         IMemAllocator_fnSetProperties,
411         IMemAllocator_fnGetProperties,
412         IMemAllocator_fnCommit,
413         IMemAllocator_fnDecommit,
414         IMemAllocator_fnGetBuffer,
415         IMemAllocator_fnReleaseBuffer,
416 };
417
418
419 HRESULT CMemoryAllocator_InitIMemAllocator( CMemoryAllocator* pma )
420 {
421         TRACE("(%p)\n",pma);
422
423         ICOM_VTBL(&pma->memalloc) = &imemalloc;
424
425         ZeroMemory( &pma->prop, sizeof(pma->prop) );
426         pma->hEventSample = (HANDLE)NULL;
427         pma->pData = NULL;
428         pma->ppSamples = NULL;
429
430         pma->hEventSample = CreateEventA( NULL, TRUE, FALSE, NULL );
431         if ( pma->hEventSample == (HANDLE)NULL )
432                 return E_OUTOFMEMORY;
433
434         InitializeCriticalSection( &pma->csMem );
435
436         return NOERROR;
437 }
438
439 void CMemoryAllocator_UninitIMemAllocator( CMemoryAllocator* pma )
440 {
441         TRACE("(%p)\n",pma);
442
443         IMemAllocator_Decommit( (IMemAllocator*)(&pma->memalloc) );
444
445         DeleteCriticalSection( &pma->csMem );
446
447         if ( pma->hEventSample != (HANDLE)NULL )
448                 CloseHandle( pma->hEventSample );
449 }
450