[PATCH v2 14/18] winegstreamer: Implement IMFMediaStream::RequestSample.

Derek Lesho dlesho at codeweavers.com
Wed Apr 1 17:05:35 CDT 2020


Signed-off-by: Derek Lesho <dlesho at codeweavers.com>
---
 dlls/mfplat/tests/mfplat.c        |   4 -
 dlls/winegstreamer/gst_cbs.c      |  10 ++
 dlls/winegstreamer/gst_cbs.h      |   6 ++
 dlls/winegstreamer/gst_private.h  |   1 +
 dlls/winegstreamer/media_source.c | 169 +++++++++++++++++++++++++++++-
 dlls/winegstreamer/mfplat.c       |  90 ++++++++++++++++
 6 files changed, 272 insertions(+), 8 deletions(-)

diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c
index b666b54cc0..cb001633e4 100644
--- a/dlls/mfplat/tests/mfplat.c
+++ b/dlls/mfplat/tests/mfplat.c
@@ -537,13 +537,10 @@ static void test_source_resolver(void)
         hr = IMFMediaStream_RequestSample(video_stream, NULL);
         if (i == sample_count)
             break;
-todo_wine
         ok(hr == S_OK, "Failed to request sample %u, hr %#x.\n", i + 1, hr);
         if (hr != S_OK)
             break;
     }
-    if (FAILED(hr))
-        goto skip_source_tests;
 
     for (i = 0; i < sample_count; ++i)
     {
@@ -584,7 +581,6 @@ todo_wine
 
     get_event((IMFMediaEventGenerator *)mediasource, MEEndOfPresentation, NULL);
 
-skip_source_tests:
     IMFMediaStream_Release(video_stream);
     IMFMediaTypeHandler_Release(handler);
     IMFPresentationDescriptor_Release(descriptor);
diff --git a/dlls/winegstreamer/gst_cbs.c b/dlls/winegstreamer/gst_cbs.c
index 5038ea3397..49c04a55e5 100644
--- a/dlls/winegstreamer/gst_cbs.c
+++ b/dlls/winegstreamer/gst_cbs.c
@@ -416,3 +416,13 @@ GstFlowReturn stream_new_sample_wrapper(GstElement *appsink, gpointer user)
 
     return cbdata.u.new_sample_data.ret;
 }
+
+void stream_eos_wrapper(GstElement *appsink, gpointer user)
+{
+    struct cb_data cbdata = { STREAM_EOS };
+
+    cbdata.u.eos_data.appsink = appsink;
+    cbdata.u.eos_data.user = user;
+
+    call_cb(&cbdata);
+}
diff --git a/dlls/winegstreamer/gst_cbs.h b/dlls/winegstreamer/gst_cbs.h
index 106368a064..4840b89b59 100644
--- a/dlls/winegstreamer/gst_cbs.h
+++ b/dlls/winegstreamer/gst_cbs.h
@@ -53,6 +53,7 @@ enum CB_TYPE {
     SOURCE_STREAM_REMOVED,
     SOURCE_ALL_STREAMS,
     STREAM_NEW_SAMPLE,
+    STREAM_EOS,
     MEDIA_SOURCE_MAX,
 };
 
@@ -143,6 +144,10 @@ struct cb_data {
             gpointer user;
             GstFlowReturn ret;
         } new_sample_data;
+        struct eos_data {
+            GstElement *appsink;
+            gpointer user;
+        } eos_data;
     } u;
 
     int finished;
@@ -179,5 +184,6 @@ void source_stream_added_wrapper(GstElement *bin, GstPad *pad, gpointer user) DE
 void source_stream_removed_wrapper(GstElement *element, GstPad *pad, gpointer user) DECLSPEC_HIDDEN;
 void source_all_streams_wrapper(GstElement *element, gpointer user) DECLSPEC_HIDDEN;
 GstFlowReturn stream_new_sample_wrapper(GstElement *appsink, gpointer user) DECLSPEC_HIDDEN;
+void stream_eos_wrapper(GstElement *appsink, gpointer user) DECLSPEC_HIDDEN;
 
 #endif
diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h
index 780cf1b02f..823e023f52 100644
--- a/dlls/winegstreamer/gst_private.h
+++ b/dlls/winegstreamer/gst_private.h
@@ -57,6 +57,7 @@ extern HRESULT mfplat_get_class_object(REFCLSID rclsid, REFIID riid, void **obj)
 
 GstCaps *make_mf_compatible_caps(GstCaps *caps);
 IMFMediaType *mf_media_type_from_caps(GstCaps *caps);
+IMFSample *mf_sample_from_gst_buffer(GstBuffer *in);
 
 enum source_type
 {
diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c
index dc1b5dca85..b0f0cf4162 100644
--- a/dlls/winegstreamer/media_source.c
+++ b/dlls/winegstreamer/media_source.c
@@ -32,6 +32,12 @@ static struct source_desc
     }
 };
 
+struct sample_request
+{
+    struct list entry;
+    IUnknown *token;
+};
+
 struct media_source;
 
 struct media_stream
@@ -52,6 +58,10 @@ struct media_stream
         STREAM_RUNNING,
         STREAM_SHUTDOWN,
     } state;
+    BOOL eos;
+    CRITICAL_SECTION dispatch_samples_cs;
+    struct list sample_requests;
+    unsigned int pending_samples;
 };
 
 struct media_source
@@ -82,6 +92,87 @@ struct media_source
 
 /* stream */
 
+static void media_source_notify_stream_ended(struct media_source *source);
+static void stream_dispatch_samples(struct media_stream *This)
+{
+    struct sample_request *req, *cursor2;
+    unsigned int fufilled_counter = 0;
+
+    if (This->state != STREAM_RUNNING && This->state != STREAM_SHUTDOWN)
+        return;
+
+    EnterCriticalSection(&This->dispatch_samples_cs);
+
+    LIST_FOR_EACH_ENTRY_SAFE(req, cursor2, &This->sample_requests, struct sample_request, entry)
+    {
+        IMFSample *sample;
+
+        if (This->state == STREAM_SHUTDOWN
+        /* Not sure if this is correct: */
+         || (!(This->pending_samples) && This->eos))
+        {
+            if (req->token)
+            {
+                IUnknown_Release(req->token);
+            }
+            list_remove(&req->entry);
+            continue;
+        }
+
+        if (!(This->pending_samples))
+        {
+            break;
+        }
+
+        /* Get the sample from the appsink, then construct an IMFSample */
+        /* We do this in the dispatch function so we can have appsink buffer for us */
+        {
+            GstSample *gst_sample;
+
+            g_signal_emit_by_name(This->appsink, "pull-sample", &gst_sample);
+            if (!gst_sample)
+            {
+                ERR("Appsink has no samples and pending_samples != 0\n");
+                break;
+            }
+
+            sample = mf_sample_from_gst_buffer(gst_sample_get_buffer(gst_sample));
+
+            gst_sample_unref(gst_sample);
+        }
+
+        if (req->token)
+        {
+            IMFSample_SetUnknown(sample, &MFSampleExtension_Token, req->token);
+        }
+
+        IMFMediaEventQueue_QueueEventParamUnk(This->event_queue, MEMediaSample, &GUID_NULL, S_OK, (IUnknown *)sample);
+
+        if (req->token)
+        {
+            IUnknown_Release(req->token);
+        }
+
+        list_remove(&req->entry);
+
+        This->pending_samples--;
+        fufilled_counter++;
+    }
+
+    TRACE("Fufilled %u sample requests. %u pending samples.  %s more requests.\n",
+            fufilled_counter, This->pending_samples, list_empty(&This->sample_requests) ? "no" : "");
+
+    if (This->eos && !This->pending_samples && This->state == STREAM_RUNNING)
+    {
+        PROPVARIANT empty;
+        empty.vt = VT_EMPTY;
+
+        IMFMediaEventQueue_QueueEventParamVar(This->event_queue, MEEndOfStream, &GUID_NULL, S_OK, &empty);
+        media_source_notify_stream_ended(This->parent_source);
+    }
+    LeaveCriticalSection(&This->dispatch_samples_cs);
+}
+
 static inline struct media_stream *impl_from_IMFMediaStream(IMFMediaStream *iface)
 {
     return CONTAINING_RECORD(iface, struct media_stream, IMFMediaStream_iface);
@@ -217,13 +308,31 @@ static HRESULT WINAPI media_stream_GetStreamDescriptor(IMFMediaStream* iface, IM
 static HRESULT WINAPI media_stream_RequestSample(IMFMediaStream *iface, IUnknown *token)
 {
     struct media_stream *This = impl_from_IMFMediaStream(iface);
+    struct sample_request *req;
 
     TRACE("(%p)->(%p)\n", iface, token);
 
     if (This->state == STREAM_SHUTDOWN)
         return MF_E_SHUTDOWN;
 
-    return E_NOTIMPL;
+    if (This->state == STREAM_INACTIVE || This->state == STREAM_ENABLED)
+    {
+        WARN("Stream isn't active\n");
+        return MF_E_MEDIA_SOURCE_WRONGSTATE;
+    }
+
+    if (This->eos && !This->pending_samples)
+        return MF_E_END_OF_STREAM;
+
+    req = heap_alloc(sizeof(*req));
+    if (token)
+        IUnknown_AddRef(token);
+    req->token = token;
+    list_add_tail(&This->sample_requests, &req->entry);
+
+    stream_dispatch_samples(This);
+
+    return S_OK;
 }
 
 static const IMFMediaStreamVtbl media_stream_vtbl =
@@ -243,15 +352,33 @@ static const IMFMediaStreamVtbl media_stream_vtbl =
 static GstFlowReturn stream_new_sample(GstElement *appsink, gpointer user)
 {
     struct media_stream *This = (struct media_stream *) user;
-    GstSample *discard_sample;
 
     TRACE("(%p) got sample\n", This);
 
-    g_signal_emit_by_name(This->appsink, "pull-sample", &discard_sample);
-    gst_sample_unref(discard_sample);
+    if (This->state == STREAM_INACTIVE)
+    {
+        GstSample *discard_sample;
+        g_signal_emit_by_name(This->appsink, "pull-sample", &discard_sample);
+        gst_sample_unref(discard_sample);
+        return GST_FLOW_OK;
+    }
+
+    This->pending_samples++;
+    stream_dispatch_samples(This);
     return GST_FLOW_OK;
 }
 
+void stream_eos(GstElement *appsink, gpointer user)
+{
+    struct media_stream *This = (struct media_stream *) user;
+
+    TRACE("(%p) EOS\n", This);
+
+    This->eos = TRUE;
+
+    stream_dispatch_samples(This);
+}
+
 static void media_stream_teardown(struct media_stream *This)
 {
     TRACE("(%p)\n", This);
@@ -262,12 +389,18 @@ static void media_stream_teardown(struct media_stream *This)
         gst_object_unref(GST_OBJECT(This->their_src));
     if (This->my_sink)
         gst_object_unref(GST_OBJECT(This->my_sink));
+
+    /* Frees pending requests and samples when state == STREAM_SHUTDOWN */
+    stream_dispatch_samples(This);
+
     if (This->descriptor)
         IMFStreamDescriptor_Release(This->descriptor);
     if (This->event_queue)
         IMFMediaEventQueue_Release(This->event_queue);
     if (This->parent_source)
         IMFMediaSource_Release(&This->parent_source->IMFMediaSource_iface);
+
+    DeleteCriticalSection(&This->dispatch_samples_cs);
 }
 
 static HRESULT media_stream_constructor(struct media_source *source, GstPad *pad, DWORD stream_id, struct media_stream **out_stream)
@@ -281,6 +414,10 @@ static HRESULT media_stream_constructor(struct media_source *source, GstPad *pad
     TRACE("(%p %p)->(%p)\n", source, pad, out_stream);
 
     This->state = STREAM_INACTIVE;
+    This->pending_samples = 0;
+    list_init(&This->sample_requests);
+    This->eos = FALSE;
+    InitializeCriticalSection(&This->dispatch_samples_cs);
 
     if (FAILED(hr = IMFMediaSource_AddRef(&source->IMFMediaSource_iface)))
     {
@@ -303,6 +440,7 @@ static HRESULT media_stream_constructor(struct media_source *source, GstPad *pad
     g_object_set(This->appsink, "emit-signals", TRUE, NULL);
     g_object_set(This->appsink, "sync", FALSE, NULL);
     g_signal_connect(This->appsink, "new-sample", G_CALLBACK(stream_new_sample_wrapper), This);
+    g_signal_connect(This->appsink, "eos", G_CALLBACK(stream_eos_wrapper), This);
 
     if (FAILED(hr = MFCreateMediaType(&media_type)))
     {
@@ -1004,6 +1142,23 @@ static void source_all_streams(GstElement *element, gpointer user)
     LeaveCriticalSection(&source->streams_cs);
 }
 
+static void media_source_notify_stream_ended(struct media_source *This)
+{
+    PROPVARIANT empty;
+    empty.vt = VT_EMPTY;
+
+    /* A stream has ended, check whether all have */
+    for (unsigned int i = 0; i < This->stream_count; i++)
+    {
+        struct media_stream *stream = This->streams[i];
+
+        if (!stream->eos)
+            return;
+    }
+
+    IMFMediaEventQueue_QueueEventParamVar(This->event_queue, MEEndOfPresentation, &GUID_NULL, S_OK, &empty);
+}
+
 static HRESULT media_source_constructor(IMFByteStream *bytestream, enum source_type type, struct media_source **out_media_source)
 {
     GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE(
@@ -1649,6 +1804,12 @@ void perform_cb_media_source(struct cb_data *cbdata)
             cbdata->u.new_sample_data.ret = stream_new_sample(data->appsink, data->user);
             break;
         }
+    case STREAM_EOS:
+        {
+            struct eos_data *data = &cbdata->u.eos_data;
+            stream_eos(data->appsink, data->user);
+            break;
+        }
     default:
         {
             ERR("Wrong callback forwarder called\n");
diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c
index 02912ac3b6..8bb030f018 100644
--- a/dlls/winegstreamer/mfplat.c
+++ b/dlls/winegstreamer/mfplat.c
@@ -871,4 +871,94 @@ GstCaps *make_mf_compatible_caps(GstCaps *caps)
     }
 
     return ret;
+}
+
+/* IMFSample = GstBuffer
+   IMFBuffer = GstMemory */
+
+/* TODO: Future optimization could be to create a custom
+   IMFMediaBuffer wrapper around GstMemory, and to utilize
+   gst_memory_new_wrapped on IMFMediaBuffer data.  However,
+   this wouldn't work if we allow the callers to allocate
+   the buffers. */
+
+IMFSample* mf_sample_from_gst_buffer(GstBuffer *gst_buffer)
+{
+    IMFSample *out = NULL;
+    LONGLONG duration, time;
+    int buffer_count;
+    HRESULT hr;
+
+    if (FAILED(hr = MFCreateSample(&out)))
+        goto fail;
+
+    duration = GST_BUFFER_DURATION(gst_buffer);
+    time = GST_BUFFER_PTS(gst_buffer);
+
+    if (FAILED(IMFSample_SetSampleDuration(out, duration / 100)))
+        goto fail;
+
+    if (FAILED(IMFSample_SetSampleTime(out, time / 100)))
+        goto fail;
+
+    buffer_count = gst_buffer_n_memory(gst_buffer);
+
+    for (unsigned int i = 0; i < buffer_count; i++)
+    {
+        GstMemory *memory = gst_buffer_get_memory(gst_buffer, i);
+        IMFMediaBuffer *mf_buffer = NULL;
+        GstMapInfo map_info;
+        BYTE *buf_data;
+
+        if (!memory)
+        {
+            hr = E_FAIL;
+            goto loop_done;
+        }
+
+        if (!(gst_memory_map(memory, &map_info, GST_MAP_READ)))
+        {
+            hr = E_FAIL;
+            goto loop_done;
+        }
+
+        if (FAILED(hr = MFCreateMemoryBuffer(map_info.maxsize, &mf_buffer)))
+        {
+            gst_memory_unmap(memory, &map_info);
+            goto loop_done;
+        }
+
+        if (FAILED(hr = IMFMediaBuffer_Lock(mf_buffer, &buf_data, NULL, NULL)))
+        {
+            gst_memory_unmap(memory, &map_info);
+            goto loop_done;
+        }
+
+        memcpy(buf_data, map_info.data, map_info.size);
+
+        gst_memory_unmap(memory, &map_info);
+
+        if (FAILED(hr = IMFMediaBuffer_Unlock(mf_buffer)))
+            goto loop_done;
+
+        if (FAILED(hr = IMFMediaBuffer_SetCurrentLength(mf_buffer, map_info.size)))
+            goto loop_done;
+
+        if (FAILED(hr = IMFSample_AddBuffer(out, mf_buffer)))
+            goto loop_done;
+
+        loop_done:
+        if (mf_buffer)
+            IMFMediaBuffer_Release(mf_buffer);
+        if (memory)
+            gst_memory_unref(memory);
+        if (FAILED(hr))
+            goto fail;
+    }
+
+    return out;
+    fail:
+    ERR("Failed to copy IMFSample to GstBuffer, hr = %#x\n", hr);
+    IMFSample_Release(out);
+    return NULL;
 }
\ No newline at end of file
-- 
2.26.0




More information about the wine-devel mailing list