From: Rémi Bernon rbernon@codeweavers.com
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/mf/tests/mf.c | 1 - dlls/winegstreamer/gst_private.h | 9 ++- dlls/winegstreamer/h264_decoder.c | 17 ++++-- dlls/winegstreamer/mfplat.c | 87 ++++++++++++++++++++++++++- dlls/winegstreamer/quartz_transform.c | 9 +++ dlls/winegstreamer/unixlib.h | 2 + dlls/winegstreamer/wg_transform.c | 28 ++++++++- dlls/winegstreamer/wma_decoder.c | 15 ++++- 8 files changed, 153 insertions(+), 15 deletions(-)
diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index 03035aa5e9f..13f0e38b0b8 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -6423,7 +6423,6 @@ static void test_wma_decoder(void) hr = IMFTransform_ProcessInput(transform, 0, sample, 0); ok(hr == MF_E_NOTACCEPTING, "ProcessInput returned %#lx\n", hr); ret = IMFSample_Release(sample); - todo_wine ok(ret == 1, "Release returned %lu\n", ret);
/* As output_info.dwFlags doesn't have MFT_OUTPUT_STREAM_CAN_PROVIDE_SAMPLES diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index ab89942847a..da76452fbf4 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -64,6 +64,12 @@ static inline const char *debugstr_time(REFERENCE_TIME time)
#define MEDIATIME_FROM_BYTES(x) ((LONGLONG)(x) * 10000000)
+struct wg_sample_queue; + +HRESULT wg_sample_queue_create(struct wg_sample_queue **out); +void wg_sample_queue_destroy(struct wg_sample_queue *queue); +void wg_sample_queue_flush(struct wg_sample_queue *queue, bool all); + struct wg_parser *wg_parser_create(enum wg_parser_type type, bool unlimited_buffering); void wg_parser_destroy(struct wg_parser *parser);
@@ -123,7 +129,8 @@ void mf_media_type_to_wg_format(IMFMediaType *type, struct wg_format *format); HRESULT wg_sample_create_mf(IMFSample *sample, struct wg_sample **out); void wg_sample_release(struct wg_sample *wg_sample);
-HRESULT wg_transform_push_mf(struct wg_transform *transform, struct wg_sample *sample); +HRESULT wg_transform_push_mf(struct wg_transform *transform, struct wg_sample *sample, + struct wg_sample_queue *queue); HRESULT wg_transform_read_mf(struct wg_transform *transform, struct wg_sample *sample, struct wg_format *format);
diff --git a/dlls/winegstreamer/h264_decoder.c b/dlls/winegstreamer/h264_decoder.c index 8d3de4355bb..012a7060f29 100644 --- a/dlls/winegstreamer/h264_decoder.c +++ b/dlls/winegstreamer/h264_decoder.c @@ -52,6 +52,7 @@ struct h264_decoder
struct wg_format wg_format; struct wg_transform *wg_transform; + struct wg_sample_queue *wg_sample_queue; };
static struct h264_decoder *impl_from_IMFTransform(IMFTransform *iface) @@ -237,6 +238,8 @@ static ULONG WINAPI transform_Release(IMFTransform *iface) IMFMediaType_Release(decoder->input_type); if (decoder->output_type) IMFMediaType_Release(decoder->output_type); + + wg_sample_queue_destroy(decoder->wg_sample_queue); free(decoder); }
@@ -543,9 +546,7 @@ static HRESULT WINAPI transform_ProcessInput(IMFTransform *iface, DWORD id, IMFS if (FAILED(hr = wg_sample_create_mf(sample, &wg_sample))) return hr;
- hr = wg_transform_push_mf(decoder->wg_transform, wg_sample); - wg_sample_release(wg_sample); - return hr; + return wg_transform_push_mf(decoder->wg_transform, wg_sample, decoder->wg_sample_queue); }
static HRESULT WINAPI transform_ProcessOutput(IMFTransform *iface, DWORD flags, DWORD count, @@ -582,7 +583,8 @@ static HRESULT WINAPI transform_ProcessOutput(IMFTransform *iface, DWORD flags, return MF_E_BUFFERTOOSMALL; }
- hr = wg_transform_read_mf(decoder->wg_transform, wg_sample, &wg_format); + if (SUCCEEDED(hr = wg_transform_read_mf(decoder->wg_transform, wg_sample, &wg_format))) + wg_sample_queue_flush(decoder->wg_sample_queue, false); wg_sample_release(wg_sample);
if (hr == MF_E_TRANSFORM_STREAM_CHANGE) @@ -648,6 +650,7 @@ HRESULT h264_decoder_create(REFIID riid, void **ret) static const struct wg_format input_format = {.major_type = WG_MAJOR_TYPE_H264}; struct wg_transform *transform; struct h264_decoder *decoder; + HRESULT hr;
TRACE("riid %s, ret %p.\n", debugstr_guid(riid), ret);
@@ -669,6 +672,12 @@ HRESULT h264_decoder_create(REFIID riid, void **ret) decoder->wg_format.u.video.fps_n = 30000; decoder->wg_format.u.video.fps_d = 1001;
+ if (FAILED(hr = wg_sample_queue_create(&decoder->wg_sample_queue))) + { + free(decoder); + return hr; + } + *ret = &decoder->IMFTransform_iface; TRACE("Created decoder %p\n", *ret); return S_OK; diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c index 40199706759..157ed3ad2f2 100644 --- a/dlls/winegstreamer/mfplat.c +++ b/dlls/winegstreamer/mfplat.c @@ -26,6 +26,7 @@ #include "mfapi.h"
#include "wine/debug.h" +#include "wine/list.h"
WINE_DEFAULT_DEBUG_CHANNEL(mfplat);
@@ -958,11 +959,18 @@ void mf_media_type_to_wg_format(IMFMediaType *type, struct wg_format *format) FIXME("Unrecognized major type %s.\n", debugstr_guid(&major_type)); }
+struct wg_sample_queue +{ + CRITICAL_SECTION cs; + struct list samples; +}; + struct mf_sample { IMFSample *sample; IMFMediaBuffer *media_buffer; struct wg_sample wg_sample; + struct list entry; };
HRESULT wg_sample_create_mf(IMFSample *sample, struct wg_sample **out) @@ -1008,14 +1016,83 @@ void wg_sample_release(struct wg_sample *wg_sample) IMFMediaBuffer_Unlock(mf_sample->media_buffer); IMFMediaBuffer_Release(mf_sample->media_buffer); IMFSample_Release(mf_sample->sample); + free(mf_sample); }
-HRESULT wg_transform_push_mf(struct wg_transform *transform, struct wg_sample *sample) +static void wg_sample_queue_begin_append(struct wg_sample_queue *queue, struct wg_sample *wg_sample) +{ + struct mf_sample *mf_sample = CONTAINING_RECORD(wg_sample, struct mf_sample, wg_sample); + + /* make sure a concurrent wg_sample_queue_flush call won't release the sample until we're done */ + InterlockedIncrement(&wg_sample->refcount); + mf_sample->wg_sample.flags |= WG_SAMPLE_FLAG_HAS_REFCOUNT; + + EnterCriticalSection(&queue->cs); + list_add_tail(&queue->samples, &mf_sample->entry); + LeaveCriticalSection(&queue->cs); +} + +static void wg_sample_queue_end_append(struct wg_sample_queue *queue, struct wg_sample *wg_sample) +{ + /* release temporary ref taken in wg_sample_queue_begin_append */ + InterlockedDecrement(&wg_sample->refcount); + + wg_sample_queue_flush(queue, false); +} + +void wg_sample_queue_flush(struct wg_sample_queue *queue, bool all) +{ + struct mf_sample *mf_sample, *next; + + EnterCriticalSection(&queue->cs); + + LIST_FOR_EACH_ENTRY_SAFE(mf_sample, next, &queue->samples, struct mf_sample, entry) + { + if (!InterlockedOr(&mf_sample->wg_sample.refcount, 0) || all) + { + list_remove(&mf_sample->entry); + wg_sample_release(&mf_sample->wg_sample); + } + } + + LeaveCriticalSection(&queue->cs); +} + +HRESULT wg_sample_queue_create(struct wg_sample_queue **out) { - struct mf_sample *mf_sample = CONTAINING_RECORD(sample, struct mf_sample, wg_sample); + struct wg_sample_queue *queue; + + if (!(queue = calloc(1, sizeof(*queue)))) + return E_OUTOFMEMORY; + + InitializeCriticalSection(&queue->cs); + queue->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": cs"); + list_init(&queue->samples); + + TRACE("Created sample queue %p\n", queue); + *out = queue; + + return S_OK; +} + +void wg_sample_queue_destroy(struct wg_sample_queue *queue) +{ + wg_sample_queue_flush(queue, true); + + queue->cs.DebugInfo->Spare[0] = 0; + InitializeCriticalSection(&queue->cs); + + free(queue); +} + +HRESULT wg_transform_push_mf(struct wg_transform *transform, struct wg_sample *wg_sample, + struct wg_sample_queue *queue) +{ + struct mf_sample *mf_sample = CONTAINING_RECORD(wg_sample, struct mf_sample, wg_sample); LONGLONG time, duration; UINT32 value; + HRESULT hr;
if (SUCCEEDED(IMFSample_GetSampleTime(mf_sample->sample, &time))) { @@ -1030,7 +1107,11 @@ HRESULT wg_transform_push_mf(struct wg_transform *transform, struct wg_sample *s if (SUCCEEDED(IMFSample_GetUINT32(mf_sample->sample, &MFSampleExtension_CleanPoint, &value)) && value) mf_sample->wg_sample.flags |= WG_SAMPLE_FLAG_SYNC_POINT;
- return wg_transform_push_data(transform, sample); + wg_sample_queue_begin_append(queue, wg_sample); + hr = wg_transform_push_data(transform, wg_sample); + wg_sample_queue_end_append(queue, wg_sample); + + return hr; }
HRESULT wg_transform_read_mf(struct wg_transform *transform, struct wg_sample *wg_sample, diff --git a/dlls/winegstreamer/quartz_transform.c b/dlls/winegstreamer/quartz_transform.c index 326b8691a42..b701b3f6369 100644 --- a/dlls/winegstreamer/quartz_transform.c +++ b/dlls/winegstreamer/quartz_transform.c @@ -40,6 +40,7 @@ struct transform IQualityControl *qc_sink;
struct wg_transform *transform; + struct wg_sample_queue *sample_queue;
const struct transform_ops *ops; }; @@ -76,6 +77,7 @@ static void transform_destroy(struct strmbase_filter *iface) strmbase_sink_cleanup(&filter->sink); strmbase_filter_cleanup(&filter->filter);
+ wg_sample_queue_destroy(filter->sample_queue); free(filter); }
@@ -572,11 +574,18 @@ static const IQualityControlVtbl source_quality_control_vtbl = static HRESULT transform_create(IUnknown *outer, const CLSID *clsid, const struct transform_ops *ops, struct transform **out) { struct transform *object; + HRESULT hr;
object = calloc(1, sizeof(*object)); if (!object) return E_OUTOFMEMORY;
+ if (FAILED(hr = wg_sample_queue_create(&object->sample_queue))) + { + free(object); + return hr; + } + strmbase_filter_init(&object->filter, outer, clsid, &filter_ops); strmbase_sink_init(&object->sink, &object->filter, L"In", &sink_ops, NULL); strmbase_source_init(&object->source, &object->filter, L"Out", &source_ops); diff --git a/dlls/winegstreamer/unixlib.h b/dlls/winegstreamer/unixlib.h index 860a8ab2a52..ed56fb47908 100644 --- a/dlls/winegstreamer/unixlib.h +++ b/dlls/winegstreamer/unixlib.h @@ -121,6 +121,8 @@ enum wg_sample_flag WG_SAMPLE_FLAG_HAS_PTS = 2, WG_SAMPLE_FLAG_HAS_DURATION = 4, WG_SAMPLE_FLAG_SYNC_POINT = 8, + + WG_SAMPLE_FLAG_HAS_REFCOUNT = 0x10000, /* sample is queued on the client side and may be wrapped */ };
struct wg_sample diff --git a/dlls/winegstreamer/wg_transform.c b/dlls/winegstreamer/wg_transform.c index f3d77f39060..d40f508250c 100644 --- a/dlls/winegstreamer/wg_transform.c +++ b/dlls/winegstreamer/wg_transform.c @@ -519,6 +519,13 @@ out: return status; }
+static void wg_sample_free_notify(void *arg) +{ + struct wg_sample *sample = arg; + GST_DEBUG("Releasing wg_sample %p", sample); + InterlockedDecrement(&sample->refcount); +} + NTSTATUS wg_transform_push_data(void *args) { struct wg_transform_push_data_params *params = args; @@ -535,12 +542,28 @@ NTSTATUS wg_transform_push_data(void *args) return STATUS_SUCCESS; }
- if (!(buffer = gst_buffer_new_and_alloc(sample->size))) + if (!(sample->flags & WG_SAMPLE_FLAG_HAS_REFCOUNT)) + { + if (!(buffer = gst_buffer_new_and_alloc(sample->size))) + { + GST_ERROR("Failed to allocate input buffer"); + return STATUS_NO_MEMORY; + } + gst_buffer_fill(buffer, 0, sample->data, sample->size); + GST_INFO("Copied %u bytes from sample %p to buffer %p", sample->size, sample, buffer); + } + else if (!(buffer = gst_buffer_new_wrapped_full(GST_MEMORY_FLAG_READONLY, sample->data, sample->max_size, + 0, sample->size, sample, wg_sample_free_notify))) { GST_ERROR("Failed to allocate input buffer"); return STATUS_NO_MEMORY; } - gst_buffer_fill(buffer, 0, sample->data, sample->size); + else + { + InterlockedIncrement(&sample->refcount); + GST_INFO("Wrapped %u/%u bytes from sample %p to buffer %p", sample->size, sample->max_size, sample, buffer); + } + if (sample->flags & WG_SAMPLE_FLAG_HAS_PTS) GST_BUFFER_PTS(buffer) = sample->pts * 100; if (sample->flags & WG_SAMPLE_FLAG_HAS_DURATION) @@ -549,7 +572,6 @@ NTSTATUS wg_transform_push_data(void *args) GST_BUFFER_FLAG_SET(buffer, GST_BUFFER_FLAG_DELTA_UNIT); gst_buffer_list_insert(transform->input, -1, buffer);
- GST_INFO("Copied %u bytes from sample %p to input buffer list", sample->size, sample); params->result = S_OK; return STATUS_SUCCESS; } diff --git a/dlls/winegstreamer/wma_decoder.c b/dlls/winegstreamer/wma_decoder.c index 81285f5ad44..e2a7a770826 100644 --- a/dlls/winegstreamer/wma_decoder.c +++ b/dlls/winegstreamer/wma_decoder.c @@ -56,6 +56,7 @@ struct wma_decoder IMFMediaType *output_type;
struct wg_transform *wg_transform; + struct wg_sample_queue *wg_sample_queue; };
static inline struct wma_decoder *impl_from_IUnknown(IUnknown *iface) @@ -135,6 +136,8 @@ static ULONG WINAPI unknown_Release(IUnknown *iface) IMFMediaType_Release(decoder->input_type); if (decoder->output_type) IMFMediaType_Release(decoder->output_type); + + wg_sample_queue_destroy(decoder->wg_sample_queue); free(decoder); }
@@ -544,9 +547,7 @@ static HRESULT WINAPI transform_ProcessInput(IMFTransform *iface, DWORD id, IMFS return S_OK; }
- hr = wg_transform_push_mf(decoder->wg_transform, wg_sample); - wg_sample_release(wg_sample); - return hr; + return wg_transform_push_mf(decoder->wg_transform, wg_sample, decoder->wg_sample_queue); }
static HRESULT WINAPI transform_ProcessOutput(IMFTransform *iface, DWORD flags, DWORD count, @@ -590,6 +591,7 @@ static HRESULT WINAPI transform_ProcessOutput(IMFTransform *iface, DWORD flags, { if (wg_sample->flags & WG_SAMPLE_FLAG_INCOMPLETE) samples[0].dwStatus |= MFT_OUTPUT_DATA_BUFFER_INCOMPLETE; + wg_sample_queue_flush(decoder->wg_sample_queue, false); }
wg_sample_release(wg_sample); @@ -882,6 +884,7 @@ HRESULT wma_decoder_create(IUnknown *outer, IUnknown **out) static const struct wg_format input_format = {.major_type = WG_MAJOR_TYPE_WMA}; struct wg_transform *transform; struct wma_decoder *decoder; + HRESULT hr;
TRACE("outer %p, out %p.\n", outer, out);
@@ -895,6 +898,12 @@ HRESULT wma_decoder_create(IUnknown *outer, IUnknown **out) if (!(decoder = calloc(1, sizeof(*decoder)))) return E_OUTOFMEMORY;
+ if (FAILED(hr = wg_sample_queue_create(&decoder->wg_sample_queue))) + { + free(decoder); + return hr; + } + decoder->IUnknown_inner.lpVtbl = &unknown_vtbl; decoder->IMFTransform_iface.lpVtbl = &transform_vtbl; decoder->IMediaObject_iface.lpVtbl = &media_object_vtbl;