cmd: DIR command outputs free space for the path.
[wine.git] / dlls / winegstreamer / wg_sample.c
blob4b02252174b3c9ce494fa7e9483f80627a15c64b
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 = (UINT_PTR)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 = (UINT_PTR)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 = (UINT_PTR)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 DeleteCriticalSection(&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(wg_transform_t transform, struct wg_sample *sample);
310 HRESULT wg_transform_read_data(wg_transform_t transform, struct wg_sample *sample,
311 struct wg_format *format);
313 HRESULT wg_transform_push_mf(wg_transform_t 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 %#I64x, 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(wg_transform_t 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 %#I64x, 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;
362 if (FAILED(hr = wg_transform_read_data(transform, wg_sample, format)))
364 if (hr == MF_E_TRANSFORM_STREAM_CHANGE && !format)
365 FIXME("Unexpected stream format change!\n");
366 wg_sample_release(wg_sample);
367 return hr;
370 if (wg_sample->flags & WG_SAMPLE_FLAG_INCOMPLETE)
371 *flags |= MFT_OUTPUT_DATA_BUFFER_INCOMPLETE;
372 if (wg_sample->flags & WG_SAMPLE_FLAG_HAS_PTS)
373 IMFSample_SetSampleTime(sample, wg_sample->pts);
374 if (wg_sample->flags & WG_SAMPLE_FLAG_HAS_DURATION)
375 IMFSample_SetSampleDuration(sample, wg_sample->duration);
376 if (wg_sample->flags & WG_SAMPLE_FLAG_SYNC_POINT)
377 IMFSample_SetUINT32(sample, &MFSampleExtension_CleanPoint, 1);
378 if (wg_sample->flags & WG_SAMPLE_FLAG_DISCONTINUITY)
379 IMFSample_SetUINT32(sample, &MFSampleExtension_Discontinuity, 1);
381 if (SUCCEEDED(hr = IMFSample_ConvertToContiguousBuffer(sample, &buffer)))
383 hr = IMFMediaBuffer_SetCurrentLength(buffer, wg_sample->size);
384 IMFMediaBuffer_Release(buffer);
387 wg_sample_release(wg_sample);
388 return hr;
391 HRESULT wg_transform_push_quartz(wg_transform_t transform, struct wg_sample *wg_sample,
392 struct wg_sample_queue *queue)
394 struct sample *sample = unsafe_quartz_from_wg_sample(wg_sample);
395 REFERENCE_TIME start_time, end_time;
396 HRESULT hr;
398 TRACE_(quartz)("transform %#I64x, wg_sample %p, queue %p.\n", transform, wg_sample, queue);
400 hr = IMediaSample_GetTime(sample->u.quartz.sample, &start_time, &end_time);
401 if (SUCCEEDED(hr))
403 wg_sample->pts = start_time;
404 wg_sample->flags |= WG_SAMPLE_FLAG_HAS_PTS;
406 if (hr == S_OK)
408 wg_sample->duration = end_time - start_time;
409 wg_sample->flags |= WG_SAMPLE_FLAG_HAS_DURATION;
412 if (IMediaSample_IsSyncPoint(sample->u.quartz.sample) == S_OK)
413 wg_sample->flags |= WG_SAMPLE_FLAG_SYNC_POINT;
414 if (IMediaSample_IsDiscontinuity(sample->u.quartz.sample) == S_OK)
415 wg_sample->flags |= WG_SAMPLE_FLAG_DISCONTINUITY;
417 wg_sample_queue_begin_append(queue, wg_sample);
418 hr = wg_transform_push_data(transform, wg_sample);
419 wg_sample_queue_end_append(queue, wg_sample);
421 return hr;
424 HRESULT wg_transform_read_quartz(wg_transform_t transform, struct wg_sample *wg_sample)
426 struct sample *sample = unsafe_quartz_from_wg_sample(wg_sample);
427 REFERENCE_TIME start_time, end_time;
428 HRESULT hr;
429 BOOL value;
431 TRACE_(mfplat)("transform %#I64x, wg_sample %p.\n", transform, wg_sample);
433 if (FAILED(hr = wg_transform_read_data(transform, wg_sample, NULL)))
435 if (hr == MF_E_TRANSFORM_STREAM_CHANGE)
436 FIXME("Unexpected stream format change!\n");
437 return hr;
440 if (FAILED(hr = IMediaSample_SetActualDataLength(sample->u.quartz.sample, wg_sample->size)))
441 return hr;
443 if (wg_sample->flags & WG_SAMPLE_FLAG_HAS_PTS)
445 start_time = wg_sample->pts;
446 if (wg_sample->flags & WG_SAMPLE_FLAG_HAS_DURATION)
448 end_time = start_time + wg_sample->duration;
449 IMediaSample_SetTime(sample->u.quartz.sample, &start_time, &end_time);
451 else
453 IMediaSample_SetTime(sample->u.quartz.sample, &start_time, NULL);
457 value = !!(wg_sample->flags & WG_SAMPLE_FLAG_SYNC_POINT);
458 IMediaSample_SetSyncPoint(sample->u.quartz.sample, value);
459 value = !!(wg_sample->flags & WG_SAMPLE_FLAG_DISCONTINUITY);
460 IMediaSample_SetDiscontinuity(sample->u.quartz.sample, value);
462 return S_OK;
465 HRESULT wg_transform_push_dmo(wg_transform_t transform, IMediaBuffer *media_buffer,
466 DWORD flags, REFERENCE_TIME time_stamp, REFERENCE_TIME time_length, struct wg_sample_queue *queue)
468 struct wg_sample *wg_sample;
469 HRESULT hr;
471 TRACE_(mfplat)("transform %#I64x, media_buffer %p, flags %#lx, time_stamp %s, time_length %s, queue %p.\n",
472 transform, media_buffer, flags, wine_dbgstr_longlong(time_stamp), wine_dbgstr_longlong(time_length), queue);
474 if (FAILED(hr = wg_sample_create_dmo(media_buffer, &wg_sample)))
475 return hr;
477 if (flags & DMO_INPUT_DATA_BUFFERF_SYNCPOINT)
478 wg_sample->flags |= WG_SAMPLE_FLAG_SYNC_POINT;
479 if (flags & DMO_INPUT_DATA_BUFFERF_TIME)
481 wg_sample->flags |= WG_SAMPLE_FLAG_HAS_PTS;
482 wg_sample->pts = time_stamp;
484 if (flags & DMO_INPUT_DATA_BUFFERF_TIMELENGTH)
486 wg_sample->flags |= WG_SAMPLE_FLAG_HAS_DURATION;
487 wg_sample->pts = time_length;
490 wg_sample_queue_begin_append(queue, wg_sample);
491 hr = wg_transform_push_data(transform, wg_sample);
492 wg_sample_queue_end_append(queue, wg_sample);
494 return hr;
497 HRESULT wg_transform_read_dmo(wg_transform_t transform, DMO_OUTPUT_DATA_BUFFER *buffer)
499 struct wg_sample *wg_sample;
500 HRESULT hr;
502 TRACE_(mfplat)("transform %#I64x, buffer %p.\n", transform, buffer);
504 if (FAILED(hr = wg_sample_create_dmo(buffer->pBuffer, &wg_sample)))
505 return hr;
506 wg_sample->size = 0;
508 if (FAILED(hr = wg_transform_read_data(transform, wg_sample, NULL)))
510 if (hr == MF_E_TRANSFORM_STREAM_CHANGE)
511 TRACE_(mfplat)("Stream format changed.\n");
512 wg_sample_release(wg_sample);
513 return hr;
516 buffer->dwStatus = 0;
517 if (wg_sample->flags & WG_SAMPLE_FLAG_INCOMPLETE)
518 buffer->dwStatus |= DMO_OUTPUT_DATA_BUFFERF_INCOMPLETE;
519 if (wg_sample->flags & WG_SAMPLE_FLAG_HAS_PTS)
521 buffer->dwStatus |= DMO_OUTPUT_DATA_BUFFERF_TIME;
522 buffer->rtTimestamp = wg_sample->pts;
524 if (wg_sample->flags & WG_SAMPLE_FLAG_HAS_DURATION)
526 buffer->dwStatus |= DMO_OUTPUT_DATA_BUFFERF_TIMELENGTH;
527 buffer->rtTimelength = wg_sample->duration;
529 if (wg_sample->flags & WG_SAMPLE_FLAG_SYNC_POINT)
530 buffer->dwStatus |= DMO_OUTPUT_DATA_BUFFERF_SYNCPOINT;
532 IMediaBuffer_SetLength(buffer->pBuffer, wg_sample->size);
534 wg_sample_release(wg_sample);
535 return hr;