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