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"
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
39 void (*destroy
)(struct wg_sample
*sample
);
44 struct wg_sample wg_sample
;
46 const struct wg_sample_ops
*ops
;
54 IMFMediaBuffer
*buffer
;
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
;
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
=
92 HRESULT
wg_sample_create_mf(IMFSample
*mf_sample
, struct wg_sample
**out
)
94 DWORD current_length
, max_length
;
95 struct sample
*sample
;
99 if (!(sample
= calloc(1, sizeof(*sample
))))
100 return E_OUTOFMEMORY
;
101 if (FAILED(hr
= IMFSample_ConvertToContiguousBuffer(mf_sample
, &sample
->u
.mf
.buffer
)))
103 if (FAILED(hr
= IMFMediaBuffer_Lock(sample
->u
.mf
.buffer
, &buffer
, &max_length
, ¤t_length
)))
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
);
117 if (sample
->u
.mf
.buffer
)
118 IMFMediaBuffer_Release(sample
->u
.mf
.buffer
);
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
;
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
;
153 if (FAILED(hr
= IMediaSample_GetPointer(media_sample
, &buffer
)))
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
;
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
;
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
=
195 HRESULT
wg_sample_create_dmo(IMediaBuffer
*media_buffer
, struct wg_sample
**out
)
197 DWORD length
, max_length
;
198 struct sample
*sample
;
202 if (!(sample
= calloc(1, sizeof(*sample
))))
203 return E_OUTOFMEMORY
;
204 if (FAILED(hr
= IMediaBuffer_GetBufferAndLength(media_buffer
, &buffer
, &length
)))
206 if (FAILED(hr
= IMediaBuffer_GetMaxLength(media_buffer
, &max_length
)))
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
);
220 if (sample
->u
.dmo
.buffer
)
221 IMediaBuffer_Release(sample
->u
.dmo
.buffer
);
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
);
236 sample
->ops
->destroy(wg_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
);
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
);
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
;
321 TRACE_(mfplat
)("transform %#I64x, sample %p, queue %p.\n", transform
, sample
, queue
);
323 if (FAILED(hr
= wg_sample_create_mf(sample
, &wg_sample
)))
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
);
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
;
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
)))
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
);
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
);
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
;
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
);
403 wg_sample
->pts
= start_time
;
404 wg_sample
->flags
|= WG_SAMPLE_FLAG_HAS_PTS
;
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
);
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
;
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");
440 if (FAILED(hr
= IMediaSample_SetActualDataLength(sample
->u
.quartz
.sample
, wg_sample
->size
)))
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
);
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
);
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
;
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
)))
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
);
497 HRESULT
wg_transform_read_dmo(wg_transform_t transform
, DMO_OUTPUT_DATA_BUFFER
*buffer
)
499 struct wg_sample
*wg_sample
;
502 TRACE_(mfplat
)("transform %#I64x, buffer %p.\n", transform
, buffer
);
504 if (FAILED(hr
= wg_sample_create_dmo(buffer
->pBuffer
, &wg_sample
)))
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
);
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
);