Sync to trunk revision 61757.
[reactos.git] / dll / directx / wine / quartz / memallocator.c
1 /*
2 * Memory Allocator and Media Sample Implementation
3 *
4 * Copyright 2003 Robert Shearman
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 */
20
21 #include "quartz_private.h"
22
23 #include <wine/list.h>
24
25 typedef struct StdMediaSample2
26 {
27 IMediaSample2 IMediaSample2_iface;
28 LONG ref;
29 AM_SAMPLE2_PROPERTIES props;
30 IMemAllocator * pParent;
31 struct list listentry;
32 LONGLONG tMediaStart;
33 LONGLONG tMediaEnd;
34 } StdMediaSample2;
35
36 typedef struct BaseMemAllocator
37 {
38 IMemAllocator IMemAllocator_iface;
39
40 LONG ref;
41 ALLOCATOR_PROPERTIES props;
42 HRESULT (* fnAlloc) (IMemAllocator *);
43 HRESULT (* fnFree)(IMemAllocator *);
44 HRESULT (* fnVerify)(IMemAllocator *, ALLOCATOR_PROPERTIES *);
45 HRESULT (* fnBufferPrepare)(IMemAllocator *, StdMediaSample2 *, DWORD flags);
46 HRESULT (* fnBufferReleased)(IMemAllocator *, StdMediaSample2 *);
47 void (* fnDestroyed)(IMemAllocator *);
48 HANDLE hSemWaiting;
49 BOOL bDecommitQueued;
50 BOOL bCommitted;
51 LONG lWaiting;
52 struct list free_list;
53 struct list used_list;
54 CRITICAL_SECTION *pCritSect;
55 } BaseMemAllocator;
56
57 static inline BaseMemAllocator *impl_from_IMemAllocator(IMemAllocator *iface)
58 {
59 return CONTAINING_RECORD(iface, BaseMemAllocator, IMemAllocator_iface);
60 }
61
62 static const IMemAllocatorVtbl BaseMemAllocator_VTable;
63 static const IMediaSample2Vtbl StdMediaSample2_VTable;
64 static inline StdMediaSample2 *unsafe_impl_from_IMediaSample(IMediaSample * iface);
65
66 #define AM_SAMPLE2_PROP_SIZE_WRITABLE FIELD_OFFSET(AM_SAMPLE2_PROPERTIES, pbBuffer)
67
68 #define INVALID_MEDIA_TIME (((ULONGLONG)0x7fffffff << 32) | 0xffffffff)
69
70 static HRESULT BaseMemAllocator_Init(HRESULT (* fnAlloc)(IMemAllocator *),
71 HRESULT (* fnFree)(IMemAllocator *),
72 HRESULT (* fnVerify)(IMemAllocator *, ALLOCATOR_PROPERTIES *),
73 HRESULT (* fnBufferPrepare)(IMemAllocator *, StdMediaSample2 *, DWORD),
74 HRESULT (* fnBufferReleased)(IMemAllocator *, StdMediaSample2 *),
75 void (* fnDestroyed)(IMemAllocator *),
76 CRITICAL_SECTION *pCritSect,
77 BaseMemAllocator * pMemAlloc)
78 {
79 assert(fnAlloc && fnFree && fnDestroyed);
80
81 pMemAlloc->IMemAllocator_iface.lpVtbl = &BaseMemAllocator_VTable;
82
83 pMemAlloc->ref = 1;
84 ZeroMemory(&pMemAlloc->props, sizeof(pMemAlloc->props));
85 list_init(&pMemAlloc->free_list);
86 list_init(&pMemAlloc->used_list);
87 pMemAlloc->fnAlloc = fnAlloc;
88 pMemAlloc->fnFree = fnFree;
89 pMemAlloc->fnVerify = fnVerify;
90 pMemAlloc->fnBufferPrepare = fnBufferPrepare;
91 pMemAlloc->fnBufferReleased = fnBufferReleased;
92 pMemAlloc->fnDestroyed = fnDestroyed;
93 pMemAlloc->bDecommitQueued = FALSE;
94 pMemAlloc->bCommitted = FALSE;
95 pMemAlloc->hSemWaiting = NULL;
96 pMemAlloc->lWaiting = 0;
97 pMemAlloc->pCritSect = pCritSect;
98
99 return S_OK;
100 }
101
102 static HRESULT WINAPI BaseMemAllocator_QueryInterface(IMemAllocator * iface, REFIID riid, LPVOID * ppv)
103 {
104 BaseMemAllocator *This = impl_from_IMemAllocator(iface);
105 TRACE("(%p)->(%s, %p)\n", This, qzdebugstr_guid(riid), ppv);
106
107 *ppv = NULL;
108
109 if (IsEqualIID(riid, &IID_IUnknown))
110 *ppv = This;
111 else if (IsEqualIID(riid, &IID_IMemAllocator))
112 *ppv = This;
113
114 if (*ppv)
115 {
116 IUnknown_AddRef((IUnknown *)(*ppv));
117 return S_OK;
118 }
119
120 FIXME("No interface for %s!\n", qzdebugstr_guid(riid));
121
122 return E_NOINTERFACE;
123 }
124
125 static ULONG WINAPI BaseMemAllocator_AddRef(IMemAllocator * iface)
126 {
127 BaseMemAllocator *This = impl_from_IMemAllocator(iface);
128 ULONG ref = InterlockedIncrement(&This->ref);
129
130 TRACE("(%p)->() AddRef from %d\n", iface, ref - 1);
131
132 return ref;
133 }
134
135 static ULONG WINAPI BaseMemAllocator_Release(IMemAllocator * iface)
136 {
137 BaseMemAllocator *This = impl_from_IMemAllocator(iface);
138 ULONG ref = InterlockedDecrement(&This->ref);
139
140 TRACE("(%p)->() Release from %d\n", iface, ref + 1);
141
142 if (!ref)
143 {
144 CloseHandle(This->hSemWaiting);
145 if (This->bCommitted)
146 This->fnFree(iface);
147
148 This->fnDestroyed(iface);
149 return 0;
150 }
151 return ref;
152 }
153
154 static HRESULT WINAPI BaseMemAllocator_SetProperties(IMemAllocator * iface, ALLOCATOR_PROPERTIES *pRequest, ALLOCATOR_PROPERTIES *pActual)
155 {
156 BaseMemAllocator *This = impl_from_IMemAllocator(iface);
157 HRESULT hr;
158
159 TRACE("(%p)->(%p, %p)\n", This, pRequest, pActual);
160
161 EnterCriticalSection(This->pCritSect);
162 {
163 if (!list_empty(&This->used_list))
164 hr = VFW_E_BUFFERS_OUTSTANDING;
165 else if (This->bCommitted)
166 hr = VFW_E_ALREADY_COMMITTED;
167 else if (pRequest->cbAlign == 0)
168 hr = VFW_E_BADALIGN;
169 else
170 {
171 if (This->fnVerify)
172 hr = This->fnVerify(iface, pRequest);
173 else
174 hr = S_OK;
175
176 if (SUCCEEDED(hr))
177 This->props = *pRequest;
178
179 *pActual = This->props;
180 }
181 }
182 LeaveCriticalSection(This->pCritSect);
183
184 return hr;
185 }
186
187 static HRESULT WINAPI BaseMemAllocator_GetProperties(IMemAllocator * iface, ALLOCATOR_PROPERTIES *pProps)
188 {
189 BaseMemAllocator *This = impl_from_IMemAllocator(iface);
190 HRESULT hr = S_OK;
191
192 TRACE("(%p)->(%p)\n", This, pProps);
193
194 EnterCriticalSection(This->pCritSect);
195 {
196 memcpy(pProps, &This->props, sizeof(*pProps));
197 }
198 LeaveCriticalSection(This->pCritSect);
199
200 return hr;
201 }
202
203 static HRESULT WINAPI BaseMemAllocator_Commit(IMemAllocator * iface)
204 {
205 BaseMemAllocator *This = impl_from_IMemAllocator(iface);
206 HRESULT hr;
207
208 TRACE("(%p)->()\n", This);
209
210 EnterCriticalSection(This->pCritSect);
211 {
212 if (!This->props.cbAlign)
213 hr = VFW_E_BADALIGN;
214 else if (!This->props.cbBuffer)
215 hr = VFW_E_SIZENOTSET;
216 else if (!This->props.cBuffers)
217 hr = VFW_E_BUFFER_NOTSET;
218 else if (This->bDecommitQueued && This->bCommitted)
219 {
220 This->bDecommitQueued = FALSE;
221 hr = S_OK;
222 }
223 else if (This->bCommitted)
224 hr = S_OK;
225 else
226 {
227 if (!(This->hSemWaiting = CreateSemaphoreW(NULL, This->props.cBuffers, This->props.cBuffers, NULL)))
228 {
229 ERR("Couldn't create semaphore (error was %u)\n", GetLastError());
230 hr = HRESULT_FROM_WIN32(GetLastError());
231 }
232 else
233 {
234 hr = This->fnAlloc(iface);
235 if (SUCCEEDED(hr))
236 This->bCommitted = TRUE;
237 else
238 ERR("fnAlloc failed with error 0x%x\n", hr);
239 }
240 }
241 }
242 LeaveCriticalSection(This->pCritSect);
243
244 return hr;
245 }
246
247 static HRESULT WINAPI BaseMemAllocator_Decommit(IMemAllocator * iface)
248 {
249 BaseMemAllocator *This = impl_from_IMemAllocator(iface);
250 HRESULT hr;
251
252 TRACE("(%p)->()\n", This);
253
254 EnterCriticalSection(This->pCritSect);
255 {
256 if (!This->bCommitted)
257 hr = S_OK;
258 else
259 {
260 if (!list_empty(&This->used_list))
261 {
262 This->bDecommitQueued = TRUE;
263 /* notify ALL waiting threads that they cannot be allocated a buffer any more */
264 ReleaseSemaphore(This->hSemWaiting, This->lWaiting, NULL);
265
266 hr = S_OK;
267 }
268 else
269 {
270 if (This->lWaiting != 0)
271 ERR("Waiting: %d\n", This->lWaiting);
272
273 This->bCommitted = FALSE;
274 CloseHandle(This->hSemWaiting);
275 This->hSemWaiting = NULL;
276
277 hr = This->fnFree(iface);
278 if (FAILED(hr))
279 ERR("fnFree failed with error 0x%x\n", hr);
280 }
281 }
282 }
283 LeaveCriticalSection(This->pCritSect);
284
285 return hr;
286 }
287
288 static HRESULT WINAPI BaseMemAllocator_GetBuffer(IMemAllocator * iface, IMediaSample ** pSample, REFERENCE_TIME *pStartTime, REFERENCE_TIME *pEndTime, DWORD dwFlags)
289 {
290 BaseMemAllocator *This = impl_from_IMemAllocator(iface);
291 HRESULT hr = S_OK;
292
293 /* NOTE: The pStartTime and pEndTime parameters are not applied to the sample.
294 * The allocator might use these values to determine which buffer it retrieves */
295
296 TRACE("(%p)->(%p, %p, %p, %x)\n", This, pSample, pStartTime, pEndTime, dwFlags);
297
298 *pSample = NULL;
299
300 EnterCriticalSection(This->pCritSect);
301 if (!This->bCommitted || This->bDecommitQueued)
302 {
303 WARN("Not committed\n");
304 hr = VFW_E_NOT_COMMITTED;
305 }
306 else
307 ++This->lWaiting;
308 LeaveCriticalSection(This->pCritSect);
309 if (FAILED(hr))
310 return hr;
311
312 if (WaitForSingleObject(This->hSemWaiting, (dwFlags & AM_GBF_NOWAIT) ? 0 : INFINITE) != WAIT_OBJECT_0)
313 {
314 EnterCriticalSection(This->pCritSect);
315 --This->lWaiting;
316 LeaveCriticalSection(This->pCritSect);
317 WARN("Timed out\n");
318 return VFW_E_TIMEOUT;
319 }
320
321 EnterCriticalSection(This->pCritSect);
322 {
323 --This->lWaiting;
324 if (!This->bCommitted)
325 hr = VFW_E_NOT_COMMITTED;
326 else if (This->bDecommitQueued)
327 hr = VFW_E_TIMEOUT;
328 else
329 {
330 StdMediaSample2 *ms;
331 struct list * free = list_head(&This->free_list);
332 list_remove(free);
333 list_add_head(&This->used_list, free);
334
335 ms = LIST_ENTRY(free, StdMediaSample2, listentry);
336 assert(ms->ref == 0);
337 *pSample = (IMediaSample *)&ms->IMediaSample2_iface;
338 IMediaSample_AddRef(*pSample);
339 }
340 }
341 LeaveCriticalSection(This->pCritSect);
342
343 if (hr != S_OK)
344 WARN("%08x\n", hr);
345 return hr;
346 }
347
348 static HRESULT WINAPI BaseMemAllocator_ReleaseBuffer(IMemAllocator * iface, IMediaSample * pSample)
349 {
350 BaseMemAllocator *This = impl_from_IMemAllocator(iface);
351 StdMediaSample2 * pStdSample = unsafe_impl_from_IMediaSample(pSample);
352 HRESULT hr = S_OK;
353
354 TRACE("(%p)->(%p)\n", This, pSample);
355
356 /* FIXME: make sure that sample is currently on the used list */
357
358 /* FIXME: we should probably check the ref count on the sample before freeing
359 * it to make sure that it is not still in use */
360 EnterCriticalSection(This->pCritSect);
361 {
362 if (!This->bCommitted)
363 ERR("Releasing a buffer when the allocator is not committed?!?\n");
364
365 /* remove from used_list */
366 list_remove(&pStdSample->listentry);
367
368 list_add_head(&This->free_list, &pStdSample->listentry);
369
370 if (list_empty(&This->used_list) && This->bDecommitQueued && This->bCommitted)
371 {
372 HRESULT hrfree;
373
374 if (This->lWaiting != 0)
375 ERR("Waiting: %d\n", This->lWaiting);
376
377 This->bCommitted = FALSE;
378 This->bDecommitQueued = FALSE;
379
380 CloseHandle(This->hSemWaiting);
381 This->hSemWaiting = NULL;
382
383 if (FAILED(hrfree = This->fnFree(iface)))
384 ERR("fnFree failed with error 0x%x\n", hrfree);
385 }
386 }
387 LeaveCriticalSection(This->pCritSect);
388
389 /* notify a waiting thread that there is now a free buffer */
390 if (This->hSemWaiting && !ReleaseSemaphore(This->hSemWaiting, 1, NULL))
391 {
392 ERR("ReleaseSemaphore failed with error %u\n", GetLastError());
393 hr = HRESULT_FROM_WIN32(GetLastError());
394 }
395
396 return hr;
397 }
398
399 static const IMemAllocatorVtbl BaseMemAllocator_VTable =
400 {
401 BaseMemAllocator_QueryInterface,
402 BaseMemAllocator_AddRef,
403 BaseMemAllocator_Release,
404 BaseMemAllocator_SetProperties,
405 BaseMemAllocator_GetProperties,
406 BaseMemAllocator_Commit,
407 BaseMemAllocator_Decommit,
408 BaseMemAllocator_GetBuffer,
409 BaseMemAllocator_ReleaseBuffer
410 };
411
412 static HRESULT StdMediaSample2_Construct(BYTE * pbBuffer, LONG cbBuffer, IMemAllocator * pParent, StdMediaSample2 ** ppSample)
413 {
414 assert(pbBuffer && pParent && (cbBuffer > 0));
415
416 if (!(*ppSample = CoTaskMemAlloc(sizeof(StdMediaSample2))))
417 return E_OUTOFMEMORY;
418
419 (*ppSample)->IMediaSample2_iface.lpVtbl = &StdMediaSample2_VTable;
420 (*ppSample)->ref = 0;
421 ZeroMemory(&(*ppSample)->props, sizeof((*ppSample)->props));
422
423 /* NOTE: no need to AddRef as the parent is guaranteed to be around
424 * at least as long as us and we don't want to create circular
425 * dependencies on the ref count */
426 (*ppSample)->pParent = pParent;
427 (*ppSample)->props.cbData = sizeof(AM_SAMPLE2_PROPERTIES);
428 (*ppSample)->props.cbBuffer = (*ppSample)->props.lActual = cbBuffer;
429 (*ppSample)->props.pbBuffer = pbBuffer;
430 (*ppSample)->tMediaStart = INVALID_MEDIA_TIME;
431 (*ppSample)->tMediaEnd = 0;
432
433 return S_OK;
434 }
435
436 static void StdMediaSample2_Delete(StdMediaSample2 * This)
437 {
438 /* NOTE: does not remove itself from the list it belongs to */
439 CoTaskMemFree(This);
440 }
441
442 static inline StdMediaSample2 *impl_from_IMediaSample2(IMediaSample2 * iface)
443 {
444 return CONTAINING_RECORD(iface, StdMediaSample2, IMediaSample2_iface);
445 }
446
447 static HRESULT WINAPI StdMediaSample2_QueryInterface(IMediaSample2 * iface, REFIID riid, void ** ppv)
448 {
449 TRACE("(%s, %p)\n", qzdebugstr_guid(riid), ppv);
450
451 *ppv = NULL;
452
453 if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IMediaSample) ||
454 IsEqualIID(riid, &IID_IMediaSample2))
455 {
456 *ppv = iface;
457 IMediaSample2_AddRef(iface);
458 return S_OK;
459 }
460
461 FIXME("No interface for %s!\n", qzdebugstr_guid(riid));
462 return E_NOINTERFACE;
463 }
464
465 static ULONG WINAPI StdMediaSample2_AddRef(IMediaSample2 * iface)
466 {
467 StdMediaSample2 *This = impl_from_IMediaSample2(iface);
468 ULONG ref = InterlockedIncrement(&This->ref);
469
470 TRACE("(%p)->(): new ref = %d\n", This, ref);
471
472 return ref;
473 }
474
475 static ULONG WINAPI StdMediaSample2_Release(IMediaSample2 * iface)
476 {
477 StdMediaSample2 *This = impl_from_IMediaSample2(iface);
478 ULONG ref = InterlockedDecrement(&This->ref);
479
480 TRACE("(%p)->(): new ref = %d\n", This, ref);
481
482 if (!ref)
483 {
484 if (This->pParent)
485 IMemAllocator_ReleaseBuffer(This->pParent, (IMediaSample *)iface);
486 else
487 StdMediaSample2_Delete(This);
488 }
489 return ref;
490 }
491
492 static HRESULT WINAPI StdMediaSample2_GetPointer(IMediaSample2 * iface, BYTE ** ppBuffer)
493 {
494 StdMediaSample2 *This = impl_from_IMediaSample2(iface);
495
496 TRACE("(%p)->(%p)\n", iface, ppBuffer);
497
498 *ppBuffer = This->props.pbBuffer;
499
500 if (!*ppBuffer)
501 {
502 ERR("Requested an unlocked surface and trying to lock regardless\n");
503 return E_FAIL;
504 }
505
506 return S_OK;
507 }
508
509 static LONG WINAPI StdMediaSample2_GetSize(IMediaSample2 * iface)
510 {
511 StdMediaSample2 *This = impl_from_IMediaSample2(iface);
512
513 TRACE("StdMediaSample2_GetSize()\n");
514
515 return This->props.cbBuffer;
516 }
517
518 static HRESULT WINAPI StdMediaSample2_GetTime(IMediaSample2 * iface, REFERENCE_TIME * pStart, REFERENCE_TIME * pEnd)
519 {
520 StdMediaSample2 *This = impl_from_IMediaSample2(iface);
521 HRESULT hr;
522
523 TRACE("(%p)->(%p, %p)\n", iface, pStart, pEnd);
524
525 if (!(This->props.dwSampleFlags & AM_SAMPLE_TIMEVALID))
526 hr = VFW_E_SAMPLE_TIME_NOT_SET;
527 else if (!(This->props.dwSampleFlags & AM_SAMPLE_STOPVALID))
528 {
529 *pStart = This->props.tStart;
530 *pEnd = This->props.tStart + 1;
531
532 hr = VFW_S_NO_STOP_TIME;
533 }
534 else
535 {
536 *pStart = This->props.tStart;
537 *pEnd = This->props.tStop;
538
539 hr = S_OK;
540 }
541
542 return hr;
543 }
544
545 static HRESULT WINAPI StdMediaSample2_SetTime(IMediaSample2 * iface, REFERENCE_TIME * pStart, REFERENCE_TIME * pEnd)
546 {
547 StdMediaSample2 *This = impl_from_IMediaSample2(iface);
548
549 TRACE("(%p)->(%p, %p)\n", iface, pStart, pEnd);
550
551 if (pStart)
552 {
553 This->props.tStart = *pStart;
554 This->props.dwSampleFlags |= AM_SAMPLE_TIMEVALID;
555 }
556 else
557 This->props.dwSampleFlags &= ~AM_SAMPLE_TIMEVALID;
558
559 if (pEnd)
560 {
561 This->props.tStop = *pEnd;
562 This->props.dwSampleFlags |= AM_SAMPLE_STOPVALID;
563 }
564 else
565 This->props.dwSampleFlags &= ~AM_SAMPLE_STOPVALID;
566
567 return S_OK;
568 }
569
570 static HRESULT WINAPI StdMediaSample2_IsSyncPoint(IMediaSample2 * iface)
571 {
572 StdMediaSample2 *This = impl_from_IMediaSample2(iface);
573
574 TRACE("(%p)->()\n", iface);
575
576 return (This->props.dwSampleFlags & AM_SAMPLE_SPLICEPOINT) ? S_OK : S_FALSE;
577 }
578
579 static HRESULT WINAPI StdMediaSample2_SetSyncPoint(IMediaSample2 * iface, BOOL bIsSyncPoint)
580 {
581 StdMediaSample2 *This = impl_from_IMediaSample2(iface);
582
583 TRACE("(%p)->(%s)\n", iface, bIsSyncPoint ? "TRUE" : "FALSE");
584
585 if (bIsSyncPoint)
586 This->props.dwSampleFlags |= AM_SAMPLE_SPLICEPOINT;
587 else
588 This->props.dwSampleFlags &= ~AM_SAMPLE_SPLICEPOINT;
589
590 return S_OK;
591 }
592
593 static HRESULT WINAPI StdMediaSample2_IsPreroll(IMediaSample2 * iface)
594 {
595 StdMediaSample2 *This = impl_from_IMediaSample2(iface);
596
597 TRACE("(%p)->()\n", iface);
598
599 return (This->props.dwSampleFlags & AM_SAMPLE_PREROLL) ? S_OK : S_FALSE;
600 }
601
602 static HRESULT WINAPI StdMediaSample2_SetPreroll(IMediaSample2 * iface, BOOL bIsPreroll)
603 {
604 StdMediaSample2 *This = impl_from_IMediaSample2(iface);
605
606 TRACE("(%p)->(%s)\n", iface, bIsPreroll ? "TRUE" : "FALSE");
607
608 if (bIsPreroll)
609 This->props.dwSampleFlags |= AM_SAMPLE_PREROLL;
610 else
611 This->props.dwSampleFlags &= ~AM_SAMPLE_PREROLL;
612
613 return S_OK;
614 }
615
616 static LONG WINAPI StdMediaSample2_GetActualDataLength(IMediaSample2 * iface)
617 {
618 StdMediaSample2 *This = impl_from_IMediaSample2(iface);
619
620 TRACE("(%p)->()\n", iface);
621
622 return This->props.lActual;
623 }
624
625 static HRESULT WINAPI StdMediaSample2_SetActualDataLength(IMediaSample2 * iface, LONG len)
626 {
627 StdMediaSample2 *This = impl_from_IMediaSample2(iface);
628
629 TRACE("(%p)->(%d)\n", iface, len);
630
631 if ((len > This->props.cbBuffer) || (len < 0))
632 {
633 WARN("Tried to set length to %d, while max is %d\n", len, This->props.cbBuffer);
634 return VFW_E_BUFFER_OVERFLOW;
635 }
636 else
637 {
638 This->props.lActual = len;
639 return S_OK;
640 }
641 }
642
643 static HRESULT WINAPI StdMediaSample2_GetMediaType(IMediaSample2 * iface, AM_MEDIA_TYPE ** ppMediaType)
644 {
645 StdMediaSample2 *This = impl_from_IMediaSample2(iface);
646
647 TRACE("(%p)->(%p)\n", iface, ppMediaType);
648
649 if (!This->props.pMediaType) {
650 /* Make sure we return a NULL pointer (required by native Quartz dll) */
651 if (ppMediaType)
652 *ppMediaType = NULL;
653 return S_FALSE;
654 }
655
656 if (!(*ppMediaType = CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE))))
657 return E_OUTOFMEMORY;
658
659 return CopyMediaType(*ppMediaType, This->props.pMediaType);
660 }
661
662 static HRESULT WINAPI StdMediaSample2_SetMediaType(IMediaSample2 * iface, AM_MEDIA_TYPE * pMediaType)
663 {
664 StdMediaSample2 *This = impl_from_IMediaSample2(iface);
665
666 TRACE("(%p)->(%p)\n", iface, pMediaType);
667
668 if (This->props.pMediaType)
669 {
670 FreeMediaType(This->props.pMediaType);
671 This->props.pMediaType = NULL;
672 }
673 if (!pMediaType)
674 return S_FALSE;
675 if (!(This->props.pMediaType = CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE))))
676 return E_OUTOFMEMORY;
677
678 return CopyMediaType(This->props.pMediaType, pMediaType);
679 }
680
681 static HRESULT WINAPI StdMediaSample2_IsDiscontinuity(IMediaSample2 * iface)
682 {
683 StdMediaSample2 *This = impl_from_IMediaSample2(iface);
684
685 TRACE("(%p)->()\n", iface);
686
687 return (This->props.dwSampleFlags & AM_SAMPLE_DATADISCONTINUITY) ? S_OK : S_FALSE;
688 }
689
690 static HRESULT WINAPI StdMediaSample2_SetDiscontinuity(IMediaSample2 * iface, BOOL bIsDiscontinuity)
691 {
692 StdMediaSample2 *This = impl_from_IMediaSample2(iface);
693
694 TRACE("(%p)->(%s)\n", iface, bIsDiscontinuity ? "TRUE" : "FALSE");
695
696 if (bIsDiscontinuity)
697 This->props.dwSampleFlags |= AM_SAMPLE_DATADISCONTINUITY;
698 else
699 This->props.dwSampleFlags &= ~AM_SAMPLE_DATADISCONTINUITY;
700
701 return S_OK;
702 }
703
704 static HRESULT WINAPI StdMediaSample2_GetMediaTime(IMediaSample2 * iface, LONGLONG * pStart, LONGLONG * pEnd)
705 {
706 StdMediaSample2 *This = impl_from_IMediaSample2(iface);
707
708 TRACE("(%p)->(%p, %p)\n", iface, pStart, pEnd);
709
710 if (This->tMediaStart == INVALID_MEDIA_TIME)
711 return VFW_E_MEDIA_TIME_NOT_SET;
712
713 *pStart = This->tMediaStart;
714 *pEnd = This->tMediaEnd;
715
716 return S_OK;
717 }
718
719 static HRESULT WINAPI StdMediaSample2_SetMediaTime(IMediaSample2 * iface, LONGLONG * pStart, LONGLONG * pEnd)
720 {
721 StdMediaSample2 *This = impl_from_IMediaSample2(iface);
722
723 TRACE("(%p)->(%p, %p)\n", iface, pStart, pEnd);
724
725 if (pStart)
726 This->tMediaStart = *pStart;
727 else
728 This->tMediaStart = INVALID_MEDIA_TIME;
729
730 if (pEnd)
731 This->tMediaEnd = *pEnd;
732 else
733 This->tMediaEnd = 0;
734
735 return S_OK;
736 }
737
738 static HRESULT WINAPI StdMediaSample2_GetProperties(IMediaSample2 * iface, DWORD cbProperties, BYTE * pbProperties)
739 {
740 StdMediaSample2 *This = impl_from_IMediaSample2(iface);
741
742 TRACE("(%p)->(%d, %p)\n", iface, cbProperties, pbProperties);
743
744 memcpy(pbProperties, &This->props, min(cbProperties, sizeof(This->props)));
745
746 return S_OK;
747 }
748
749 static HRESULT WINAPI StdMediaSample2_SetProperties(IMediaSample2 * iface, DWORD cbProperties, const BYTE * pbProperties)
750 {
751 StdMediaSample2 *This = impl_from_IMediaSample2(iface);
752
753 TRACE("(%p)->(%d, %p)\n", iface, cbProperties, pbProperties);
754
755 /* NOTE: pbBuffer and cbBuffer are read-only */
756 memcpy(&This->props, pbProperties, min(cbProperties, AM_SAMPLE2_PROP_SIZE_WRITABLE));
757
758 return S_OK;
759 }
760
761 static const IMediaSample2Vtbl StdMediaSample2_VTable =
762 {
763 StdMediaSample2_QueryInterface,
764 StdMediaSample2_AddRef,
765 StdMediaSample2_Release,
766 StdMediaSample2_GetPointer,
767 StdMediaSample2_GetSize,
768 StdMediaSample2_GetTime,
769 StdMediaSample2_SetTime,
770 StdMediaSample2_IsSyncPoint,
771 StdMediaSample2_SetSyncPoint,
772 StdMediaSample2_IsPreroll,
773 StdMediaSample2_SetPreroll,
774 StdMediaSample2_GetActualDataLength,
775 StdMediaSample2_SetActualDataLength,
776 StdMediaSample2_GetMediaType,
777 StdMediaSample2_SetMediaType,
778 StdMediaSample2_IsDiscontinuity,
779 StdMediaSample2_SetDiscontinuity,
780 StdMediaSample2_GetMediaTime,
781 StdMediaSample2_SetMediaTime,
782 StdMediaSample2_GetProperties,
783 StdMediaSample2_SetProperties
784 };
785
786 static inline StdMediaSample2 *unsafe_impl_from_IMediaSample(IMediaSample * iface)
787 {
788 IMediaSample2 *iface2 = (IMediaSample2 *)iface;
789
790 if (!iface)
791 return NULL;
792 assert(iface2->lpVtbl == &StdMediaSample2_VTable);
793 return impl_from_IMediaSample2(iface2);
794 }
795
796 typedef struct StdMemAllocator
797 {
798 BaseMemAllocator base;
799 CRITICAL_SECTION csState;
800 LPVOID pMemory;
801 } StdMemAllocator;
802
803 static inline StdMemAllocator *StdMemAllocator_from_IMemAllocator(IMemAllocator * iface)
804 {
805 return CONTAINING_RECORD(iface, StdMemAllocator, base.IMemAllocator_iface);
806 }
807
808 static HRESULT StdMemAllocator_Alloc(IMemAllocator * iface)
809 {
810 StdMemAllocator *This = StdMemAllocator_from_IMemAllocator(iface);
811 StdMediaSample2 * pSample = NULL;
812 SYSTEM_INFO si;
813 LONG i;
814
815 assert(list_empty(&This->base.free_list));
816
817 /* check alignment */
818 GetSystemInfo(&si);
819
820 /* we do not allow a courser alignment than the OS page size */
821 if ((si.dwPageSize % This->base.props.cbAlign) != 0)
822 return VFW_E_BADALIGN;
823
824 /* FIXME: each sample has to have its buffer start on the right alignment.
825 * We don't do this at the moment */
826
827 /* allocate memory */
828 This->pMemory = VirtualAlloc(NULL, (This->base.props.cbBuffer + This->base.props.cbPrefix) * This->base.props.cBuffers, MEM_COMMIT, PAGE_READWRITE);
829
830 if (!This->pMemory)
831 return E_OUTOFMEMORY;
832
833 for (i = This->base.props.cBuffers - 1; i >= 0; i--)
834 {
835 /* pbBuffer does not start at the base address, it starts at base + cbPrefix */
836 BYTE * pbBuffer = (BYTE *)This->pMemory + i * (This->base.props.cbBuffer + This->base.props.cbPrefix) + This->base.props.cbPrefix;
837
838 StdMediaSample2_Construct(pbBuffer, This->base.props.cbBuffer, iface, &pSample);
839
840 list_add_head(&This->base.free_list, &pSample->listentry);
841 }
842
843 return S_OK;
844 }
845
846 static HRESULT StdMemAllocator_Free(IMemAllocator * iface)
847 {
848 StdMemAllocator *This = StdMemAllocator_from_IMemAllocator(iface);
849 struct list * cursor;
850
851 if (!list_empty(&This->base.used_list))
852 {
853 WARN("Freeing allocator with outstanding samples!\n");
854 while ((cursor = list_head(&This->base.used_list)) != NULL)
855 {
856 StdMediaSample2 *pSample;
857 list_remove(cursor);
858 pSample = LIST_ENTRY(cursor, StdMediaSample2, listentry);
859 pSample->pParent = NULL;
860 }
861 }
862
863 while ((cursor = list_head(&This->base.free_list)) != NULL)
864 {
865 list_remove(cursor);
866 StdMediaSample2_Delete(LIST_ENTRY(cursor, StdMediaSample2, listentry));
867 }
868
869 /* free memory */
870 if (!VirtualFree(This->pMemory, 0, MEM_RELEASE))
871 {
872 ERR("Couldn't free memory. Error: %u\n", GetLastError());
873 return HRESULT_FROM_WIN32(GetLastError());
874 }
875
876 return S_OK;
877 }
878
879 static void StdMemAllocator_Destroy(IMemAllocator *iface)
880 {
881 StdMemAllocator *This = StdMemAllocator_from_IMemAllocator(iface);
882
883 This->csState.DebugInfo->Spare[0] = 0;
884 DeleteCriticalSection(&This->csState);
885
886 CoTaskMemFree(This);
887 }
888
889 HRESULT StdMemAllocator_create(LPUNKNOWN lpUnkOuter, LPVOID * ppv)
890 {
891 StdMemAllocator * pMemAlloc;
892 HRESULT hr;
893
894 *ppv = NULL;
895
896 if (lpUnkOuter)
897 return CLASS_E_NOAGGREGATION;
898
899 if (!(pMemAlloc = CoTaskMemAlloc(sizeof(*pMemAlloc))))
900 return E_OUTOFMEMORY;
901
902 InitializeCriticalSection(&pMemAlloc->csState);
903 pMemAlloc->csState.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": StdMemAllocator.csState");
904
905 pMemAlloc->pMemory = NULL;
906
907 if (SUCCEEDED(hr = BaseMemAllocator_Init(StdMemAllocator_Alloc, StdMemAllocator_Free, NULL, NULL, NULL, StdMemAllocator_Destroy, &pMemAlloc->csState, &pMemAlloc->base)))
908 *ppv = pMemAlloc;
909 else
910 CoTaskMemFree(pMemAlloc);
911
912 return hr;
913 }