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