ntdll: Use a separate memory allocation for the kernel stack.
[wine.git] / dlls / winegstreamer / wg_sample.c
blobd4bb7a8b954e8b4bbe15cf924775055ac44c03d7
1 /*
2 * Copyright 2022 RĂ©mi Bernon for CodeWeavers
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.
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.
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 #include "gst_private.h"
21 #include "wmcodecdsp.h"
22 #include "mfapi.h"
23 #include "mferror.h"
25 #include "wine/debug.h"
26 #include "wine/list.h"
28 WINE_DEFAULT_DEBUG_CHANNEL(mfplat);
29 WINE_DECLARE_DEBUG_CHANNEL(quartz);
31 struct wg_sample_queue
33 CRITICAL_SECTION cs;
34 struct list samples;
37 struct wg_sample_ops
39 void (*destroy)(struct wg_sample *sample);
42 struct sample
44 struct wg_sample wg_sample;
46 const struct wg_sample_ops *ops;
47 struct list entry;
49 union
51 struct
53 IMFSample *sample;
54 IMFMediaBuffer *buffer;
55 } mf;
56 struct
58 IMediaSample *sample;
59 } quartz;
60 struct
62 IMediaBuffer *buffer;
63 } dmo;
64 } u;
67 static const struct wg_sample_ops mf_sample_ops;
69 static inline struct sample *unsafe_mf_from_wg_sample(struct wg_sample *wg_sample)
71 struct sample *sample = CONTAINING_RECORD(wg_sample, struct sample, wg_sample);
72 if (sample->ops != &mf_sample_ops) return NULL;
73 return sample;
76 static void mf_sample_destroy(struct wg_sample *wg_sample)
78 struct sample *sample = unsafe_mf_from_wg_sample(wg_sample);
80 TRACE_(mfplat)("wg_sample %p.\n", wg_sample);
82 IMFMediaBuffer_Unlock(sample->u.mf.buffer);
83 IMFMediaBuffer_Release(sample->u.mf.buffer);
84 IMFSample_Release(sample->u.mf.sample);
87 static const struct wg_sample_ops mf_sample_ops =
89 mf_sample_destroy,
92 HRESULT wg_sample_create_mf(IMFSample *mf_sample, struct wg_sample **out)
94 DWORD current_length, max_length;
95 struct sample *sample;
96 BYTE *buffer;
97 HRESULT hr;
99 if (!(sample = calloc(1, sizeof(*sample))))
100 return E_OUTOFMEMORY;
101 if (FAILED(hr = IMFSample_ConvertToContiguousBuffer(mf_sample, &sample->u.mf.buffer)))
102 goto fail;
103 if (FAILED(hr = IMFMediaBuffer_Lock(sample->u.mf.buffer, &buffer, &max_length, &current_length)))
104 goto fail;
106 IMFSample_AddRef((sample->u.mf.sample = mf_sample));
107 sample->wg_sample.data = buffer;
108 sample->wg_sample.size = current_length;
109 sample->wg_sample.max_size = max_length;
110 sample->ops = &mf_sample_ops;
112 *out = &sample->wg_sample;
113 TRACE_(mfplat)("Created wg_sample %p for IMFSample %p.\n", *out, mf_sample);
114 return S_OK;
116 fail:
117 if (sample->u.mf.buffer)
118 IMFMediaBuffer_Release(sample->u.mf.buffer);
119 free(sample);
120 return hr;
123 static const struct wg_sample_ops quartz_sample_ops;
125 static inline struct sample *unsafe_quartz_from_wg_sample(struct wg_sample *wg_sample)
127 struct sample *sample = CONTAINING_RECORD(wg_sample, struct sample, wg_sample);
128 if (sample->ops != &quartz_sample_ops) return NULL;
129 return sample;
132 static void quartz_sample_destroy(struct wg_sample *wg_sample)
134 struct sample *sample = unsafe_quartz_from_wg_sample(wg_sample);
136 TRACE_(quartz)("wg_sample %p.\n", wg_sample);
138 IMediaSample_Release(sample->u.quartz.sample);
141 static const struct wg_sample_ops quartz_sample_ops =
143 quartz_sample_destroy,
146 HRESULT wg_sample_create_quartz(IMediaSample *media_sample, struct wg_sample **out)
148 DWORD current_length, max_length;
149 struct sample *sample;
150 BYTE *buffer;
151 HRESULT hr;
153 if (FAILED(hr = IMediaSample_GetPointer(media_sample, &buffer)))
154 return hr;
155 current_length = IMediaSample_GetActualDataLength(media_sample);
156 max_length = IMediaSample_GetSize(media_sample);
158 if (!(sample = calloc(1, sizeof(*sample))))
159 return E_OUTOFMEMORY;
161 IMediaSample_AddRef((sample->u.quartz.sample = media_sample));
162 sample->wg_sample.data = buffer;
163 sample->wg_sample.size = current_length;
164 sample->wg_sample.max_size = max_length;
165 sample->ops = &quartz_sample_ops;
167 TRACE_(quartz)("Created wg_sample %p for IMediaSample %p.\n", &sample->wg_sample, media_sample);
168 *out = &sample->wg_sample;
169 return S_OK;
172 static const struct wg_sample_ops dmo_sample_ops;
174 static inline struct sample *unsafe_dmo_from_wg_sample(struct wg_sample *wg_sample)
176 struct sample *sample = CONTAINING_RECORD(wg_sample, struct sample, wg_sample);
177 if (sample->ops != &dmo_sample_ops) return NULL;
178 return sample;
181 static void dmo_sample_destroy(struct wg_sample *wg_sample)
183 struct sample *sample = unsafe_dmo_from_wg_sample(wg_sample);
185 TRACE_(mfplat)("wg_sample %p.\n", wg_sample);
187 IMediaBuffer_Release(sample->u.dmo.buffer);
190 static const struct wg_sample_ops dmo_sample_ops =
192 dmo_sample_destroy,
195 HRESULT wg_sample_create_dmo(IMediaBuffer *media_buffer, struct wg_sample **out)
197 DWORD length, max_length;
198 struct sample *sample;
199 BYTE *buffer;
200 HRESULT hr;
202 if (!(sample = calloc(1, sizeof(*sample))))
203 return E_OUTOFMEMORY;
204 if (FAILED(hr = IMediaBuffer_GetBufferAndLength(media_buffer, &buffer, &length)))
205 goto fail;
206 if (FAILED(hr = IMediaBuffer_GetMaxLength(media_buffer, &max_length)))
207 goto fail;
209 IMediaBuffer_AddRef((sample->u.dmo.buffer = media_buffer));
210 sample->wg_sample.data = buffer;
211 sample->wg_sample.size = length;
212 sample->wg_sample.max_size = max_length;
213 sample->ops = &dmo_sample_ops;
215 *out = &sample->wg_sample;
216 TRACE_(mfplat)("Created wg_sample %p for IMediaBuffer %p.\n", *out, media_buffer);
217 return S_OK;
219 fail:
220 if (sample->u.dmo.buffer)
221 IMediaBuffer_Release(sample->u.dmo.buffer);
222 free(sample);
223 return hr;
226 void wg_sample_release(struct wg_sample *wg_sample)
228 struct sample *sample = CONTAINING_RECORD(wg_sample, struct sample, wg_sample);
230 if (InterlockedOr(&wg_sample->refcount, 0))
232 ERR("wg_sample %p is still in use, trouble ahead!\n", wg_sample);
233 return;
236 sample->ops->destroy(wg_sample);
238 free(sample);
241 static void wg_sample_queue_begin_append(struct wg_sample_queue *queue, struct wg_sample *wg_sample)
243 struct sample *sample = CONTAINING_RECORD(wg_sample, struct sample, wg_sample);
245 /* make sure a concurrent wg_sample_queue_flush call won't release the sample until we're done */
246 InterlockedIncrement(&wg_sample->refcount);
248 EnterCriticalSection(&queue->cs);
249 list_add_tail(&queue->samples, &sample->entry);
250 LeaveCriticalSection(&queue->cs);
253 static void wg_sample_queue_end_append(struct wg_sample_queue *queue, struct wg_sample *wg_sample)
255 /* release temporary ref taken in wg_sample_queue_begin_append */
256 InterlockedDecrement(&wg_sample->refcount);
258 wg_sample_queue_flush(queue, false);
261 void wg_sample_queue_flush(struct wg_sample_queue *queue, bool all)
263 struct sample *sample, *next;
265 EnterCriticalSection(&queue->cs);
267 LIST_FOR_EACH_ENTRY_SAFE(sample, next, &queue->samples, struct sample, entry)
269 if (!InterlockedOr(&sample->wg_sample.refcount, 0) || all)
271 list_remove(&sample->entry);
272 wg_sample_release(&sample->wg_sample);
276 LeaveCriticalSection(&queue->cs);
279 HRESULT wg_sample_queue_create(struct wg_sample_queue **out)
281 struct wg_sample_queue *queue;
283 if (!(queue = calloc(1, sizeof(*queue))))
284 return E_OUTOFMEMORY;
286 InitializeCriticalSection(&queue->cs);
287 queue->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": cs");
288 list_init(&queue->samples);
290 TRACE("Created wg_sample_queue %p.\n", queue);
291 *out = queue;
293 return S_OK;
296 void wg_sample_queue_destroy(struct wg_sample_queue *queue)
298 wg_sample_queue_flush(queue, true);
300 queue->cs.DebugInfo->Spare[0] = 0;
301 InitializeCriticalSection(&queue->cs);
303 free(queue);
306 /* These unixlib entry points should not be used directly, they assume samples
307 * to be queued and zero-copy support, use the helpers below instead.
309 HRESULT wg_transform_push_data(struct wg_transform *transform, struct wg_sample *sample);
310 HRESULT wg_transform_read_data(struct wg_transform *transform, struct wg_sample *sample,
311 struct wg_format *format);
313 HRESULT wg_transform_push_mf(struct wg_transform *transform, IMFSample *sample,
314 struct wg_sample_queue *queue)
316 struct wg_sample *wg_sample;
317 LONGLONG time, duration;
318 UINT32 value;
319 HRESULT hr;
321 TRACE_(mfplat)("transform %p, sample %p, queue %p.\n", transform, sample, queue);
323 if (FAILED(hr = wg_sample_create_mf(sample, &wg_sample)))
324 return hr;
326 if (SUCCEEDED(IMFSample_GetSampleTime(sample, &time)))
328 wg_sample->flags |= WG_SAMPLE_FLAG_HAS_PTS;
329 wg_sample->pts = time;
331 if (SUCCEEDED(IMFSample_GetSampleDuration(sample, &duration)))
333 wg_sample->flags |= WG_SAMPLE_FLAG_HAS_DURATION;
334 wg_sample->duration = duration;
336 if (SUCCEEDED(IMFSample_GetUINT32(sample, &MFSampleExtension_CleanPoint, &value)) && value)
337 wg_sample->flags |= WG_SAMPLE_FLAG_SYNC_POINT;
338 if (SUCCEEDED(IMFSample_GetUINT32(sample, &MFSampleExtension_Discontinuity, &value)) && value)
339 wg_sample->flags |= WG_SAMPLE_FLAG_DISCONTINUITY;
341 wg_sample_queue_begin_append(queue, wg_sample);
342 hr = wg_transform_push_data(transform, wg_sample);
343 wg_sample_queue_end_append(queue, wg_sample);
345 return hr;
348 HRESULT wg_transform_read_mf(struct wg_transform *transform, IMFSample *sample,
349 DWORD sample_size, struct wg_format *format, DWORD *flags)
351 struct wg_sample *wg_sample;
352 IMFMediaBuffer *buffer;
353 HRESULT hr;
355 TRACE_(mfplat)("transform %p, sample %p, format %p, flags %p.\n", transform, sample, format, flags);
357 if (FAILED(hr = wg_sample_create_mf(sample, &wg_sample)))
358 return hr;
360 wg_sample->size = 0;
361 if (wg_sample->max_size < sample_size)
363 wg_sample_release(wg_sample);
364 return MF_E_BUFFERTOOSMALL;
367 if (FAILED(hr = wg_transform_read_data(transform, wg_sample, format)))
369 if (hr == MF_E_TRANSFORM_STREAM_CHANGE && !format)
370 FIXME("Unexpected stream format change!\n");
371 wg_sample_release(wg_sample);
372 return hr;
375 if (wg_sample->flags & WG_SAMPLE_FLAG_INCOMPLETE)
376 *flags |= MFT_OUTPUT_DATA_BUFFER_INCOMPLETE;
377 if (wg_sample->flags & WG_SAMPLE_FLAG_HAS_PTS)
378 IMFSample_SetSampleTime(sample, wg_sample->pts);
379 if (wg_sample->flags & WG_SAMPLE_FLAG_HAS_DURATION)
380 IMFSample_SetSampleDuration(sample, wg_sample->duration);
381 if (wg_sample->flags & WG_SAMPLE_FLAG_SYNC_POINT)
382 IMFSample_SetUINT32(sample, &MFSampleExtension_CleanPoint, 1);
383 if (wg_sample->flags & WG_SAMPLE_FLAG_DISCONTINUITY)
384 IMFSample_SetUINT32(sample, &MFSampleExtension_Discontinuity, 1);
386 if (SUCCEEDED(hr = IMFSample_ConvertToContiguousBuffer(sample, &buffer)))
388 hr = IMFMediaBuffer_SetCurrentLength(buffer, wg_sample->size);
389 IMFMediaBuffer_Release(buffer);
392 wg_sample_release(wg_sample);
393 return hr;
396 HRESULT wg_transform_push_quartz(struct wg_transform *transform, struct wg_sample *wg_sample,
397 struct wg_sample_queue *queue)
399 struct sample *sample = unsafe_quartz_from_wg_sample(wg_sample);
400 REFERENCE_TIME start_time, end_time;
401 HRESULT hr;
403 TRACE_(quartz)("transform %p, wg_sample %p, queue %p.\n", transform, wg_sample, queue);
405 hr = IMediaSample_GetTime(sample->u.quartz.sample, &start_time, &end_time);
406 if (SUCCEEDED(hr))
408 wg_sample->pts = start_time;
409 wg_sample->flags |= WG_SAMPLE_FLAG_HAS_PTS;
411 if (hr == S_OK)
413 wg_sample->duration = end_time - start_time;
414 wg_sample->flags |= WG_SAMPLE_FLAG_HAS_DURATION;
417 if (IMediaSample_IsSyncPoint(sample->u.quartz.sample) == S_OK)
418 wg_sample->flags |= WG_SAMPLE_FLAG_SYNC_POINT;
419 if (IMediaSample_IsDiscontinuity(sample->u.quartz.sample) == S_OK)
420 wg_sample->flags |= WG_SAMPLE_FLAG_DISCONTINUITY;
422 wg_sample_queue_begin_append(queue, wg_sample);
423 hr = wg_transform_push_data(transform, wg_sample);
424 wg_sample_queue_end_append(queue, wg_sample);
426 return hr;
429 HRESULT wg_transform_read_quartz(struct wg_transform *transform, struct wg_sample *wg_sample)
431 struct sample *sample = unsafe_quartz_from_wg_sample(wg_sample);
432 REFERENCE_TIME start_time, end_time;
433 HRESULT hr;
434 BOOL value;
436 TRACE_(mfplat)("transform %p, wg_sample %p.\n", transform, wg_sample);
438 if (FAILED(hr = wg_transform_read_data(transform, wg_sample, NULL)))
440 if (hr == MF_E_TRANSFORM_STREAM_CHANGE)
441 FIXME("Unexpected stream format change!\n");
442 return hr;
445 if (FAILED(hr = IMediaSample_SetActualDataLength(sample->u.quartz.sample, wg_sample->size)))
446 return hr;
448 if (wg_sample->flags & WG_SAMPLE_FLAG_HAS_PTS)
450 start_time = wg_sample->pts;
451 if (wg_sample->flags & WG_SAMPLE_FLAG_HAS_DURATION)
453 end_time = start_time + wg_sample->duration;
454 IMediaSample_SetTime(sample->u.quartz.sample, &start_time, &end_time);
456 else
458 IMediaSample_SetTime(sample->u.quartz.sample, &start_time, NULL);
462 value = !!(wg_sample->flags & WG_SAMPLE_FLAG_SYNC_POINT);
463 IMediaSample_SetSyncPoint(sample->u.quartz.sample, value);
464 value = !!(wg_sample->flags & WG_SAMPLE_FLAG_DISCONTINUITY);
465 IMediaSample_SetDiscontinuity(sample->u.quartz.sample, value);
467 return S_OK;
470 HRESULT wg_transform_push_dmo(struct wg_transform *transform, IMediaBuffer *media_buffer,
471 DWORD flags, REFERENCE_TIME time_stamp, REFERENCE_TIME time_length, struct wg_sample_queue *queue)
473 struct wg_sample *wg_sample;
474 HRESULT hr;
476 TRACE_(mfplat)("transform %p, media_buffer %p, flags %#lx, time_stamp %s, time_length %s, queue %p.\n",
477 transform, media_buffer, flags, wine_dbgstr_longlong(time_stamp), wine_dbgstr_longlong(time_length), queue);
479 if (FAILED(hr = wg_sample_create_dmo(media_buffer, &wg_sample)))
480 return hr;
482 if (flags & DMO_INPUT_DATA_BUFFERF_SYNCPOINT)
483 wg_sample->flags |= WG_SAMPLE_FLAG_SYNC_POINT;
484 if (flags & DMO_INPUT_DATA_BUFFERF_TIME)
486 wg_sample->flags |= WG_SAMPLE_FLAG_HAS_PTS;
487 wg_sample->pts = time_stamp;
489 if (flags & DMO_INPUT_DATA_BUFFERF_TIMELENGTH)
491 wg_sample->flags |= WG_SAMPLE_FLAG_HAS_DURATION;
492 wg_sample->pts = time_length;
495 wg_sample_queue_begin_append(queue, wg_sample);
496 hr = wg_transform_push_data(transform, wg_sample);
497 wg_sample_queue_end_append(queue, wg_sample);
499 return hr;
502 HRESULT wg_transform_read_dmo(struct wg_transform *transform, DMO_OUTPUT_DATA_BUFFER *buffer)
504 struct wg_sample *wg_sample;
505 HRESULT hr;
507 TRACE_(mfplat)("transform %p, buffer %p.\n", transform, buffer);
509 if (FAILED(hr = wg_sample_create_dmo(buffer->pBuffer, &wg_sample)))
510 return hr;
511 wg_sample->size = 0;
513 if (FAILED(hr = wg_transform_read_data(transform, wg_sample, NULL)))
515 if (hr == MF_E_TRANSFORM_STREAM_CHANGE)
516 TRACE_(mfplat)("Stream format changed.\n");
517 wg_sample_release(wg_sample);
518 return hr;
521 buffer->dwStatus = 0;
522 if (wg_sample->flags & WG_SAMPLE_FLAG_INCOMPLETE)
523 buffer->dwStatus |= DMO_OUTPUT_DATA_BUFFERF_INCOMPLETE;
524 if (wg_sample->flags & WG_SAMPLE_FLAG_HAS_PTS)
526 buffer->dwStatus |= DMO_OUTPUT_DATA_BUFFERF_TIME;
527 buffer->rtTimestamp = wg_sample->pts;
529 if (wg_sample->flags & WG_SAMPLE_FLAG_HAS_DURATION)
531 buffer->dwStatus |= DMO_OUTPUT_DATA_BUFFERF_TIMELENGTH;
532 buffer->rtTimelength = wg_sample->duration;
534 if (wg_sample->flags & WG_SAMPLE_FLAG_SYNC_POINT)
535 buffer->dwStatus |= DMO_OUTPUT_DATA_BUFFERF_SYNCPOINT;
537 IMediaBuffer_SetLength(buffer->pBuffer, wg_sample->size);
539 wg_sample_release(wg_sample);
540 return hr;