[PATCH v2 5/5] winegstreamer: Implement IMFMediaStream::RequestSample.

Derek Lesho dlesho at codeweavers.com
Tue Oct 6 10:59:53 CDT 2020


Signed-off-by: Derek Lesho <dlesho at codeweavers.com>
---
v2: Addressed comments.
---
 dlls/mfplat/tests/mfplat.c        |  4 --
 dlls/winegstreamer/gst_private.h  |  1 +
 dlls/winegstreamer/media_source.c | 89 ++++++++++++++++++++++++++++++-
 dlls/winegstreamer/mfplat.c       | 69 ++++++++++++++++++++++++
 4 files changed, 158 insertions(+), 5 deletions(-)

diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c
index bffce2bc114..c54113e5588 100644
--- a/dlls/mfplat/tests/mfplat.c
+++ b/dlls/mfplat/tests/mfplat.c
@@ -623,13 +623,10 @@ todo_wine
         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)
     {
@@ -670,7 +667,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_private.h b/dlls/winegstreamer/gst_private.h
index 07556802a51..ff5aff42482 100644
--- a/dlls/winegstreamer/gst_private.h
+++ b/dlls/winegstreamer/gst_private.h
@@ -58,6 +58,7 @@ extern HRESULT mfplat_get_class_object(REFCLSID rclsid, REFIID riid, void **obj)
 HRESULT winegstreamer_stream_handler_create(REFIID riid, void **obj) DECLSPEC_HIDDEN;
 IMFMediaType *mf_media_type_from_caps(const GstCaps *caps) DECLSPEC_HIDDEN;
 GstCaps *caps_from_mf_media_type(IMFMediaType *type) DECLSPEC_HIDDEN;
+IMFSample *mf_sample_from_gst_buffer(GstBuffer *in) DECLSPEC_HIDDEN;
 
 HRESULT winegstreamer_stream_handler_create(REFIID riid, void **obj) DECLSPEC_HIDDEN;
 
diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c
index 5eb4465fea6..1593a60dfe1 100644
--- a/dlls/winegstreamer/media_source.c
+++ b/dlls/winegstreamer/media_source.c
@@ -64,6 +64,7 @@ struct media_stream
 enum source_async_op
 {
     SOURCE_ASYNC_START,
+    SOURCE_ASYNC_REQUEST_SAMPLE,
 };
 
 struct source_async_command
@@ -79,6 +80,11 @@ struct source_async_command
             GUID format;
             PROPVARIANT position;
         } start;
+        struct
+        {
+            struct media_stream *stream;
+            IUnknown *token;
+        } request_sample;
     } u;
 };
 
@@ -341,6 +347,59 @@ static void start_pipeline(struct media_source *source, struct source_async_comm
     gst_element_get_state(source->container, NULL, NULL, -1);
 }
 
+static void dispatch_end_of_presentation(struct media_source *source)
+{
+    PROPVARIANT empty = {.vt = VT_EMPTY};
+    unsigned int i;
+
+    /* A stream has ended, check whether all have */
+    for (i = 0; i < source->stream_count; i++)
+    {
+        struct media_stream *stream = source->streams[i];
+
+        if (stream->state != STREAM_INACTIVE && !stream->eos)
+            return;
+    }
+
+    IMFMediaEventQueue_QueueEventParamVar(source->event_queue, MEEndOfPresentation, &GUID_NULL, S_OK, &empty);
+}
+
+static void wait_on_sample(struct media_stream *stream, IUnknown *token)
+{
+    PROPVARIANT empty_var = {.vt = VT_EMPTY};
+    GstSample *gst_sample;
+    GstBuffer *buffer;
+    IMFSample *sample;
+
+    TRACE("%p, %p\n", stream, token);
+
+    g_signal_emit_by_name(stream->appsink, "pull-sample", &gst_sample);
+    if (gst_sample)
+    {
+        buffer = gst_sample_get_buffer(gst_sample);
+
+        TRACE("PTS = %llu\n", (unsigned long long int) GST_BUFFER_PTS(buffer));
+
+        sample = mf_sample_from_gst_buffer(buffer);
+        gst_sample_unref(gst_sample);
+
+        if (token)
+            IMFSample_SetUnknown(sample, &MFSampleExtension_Token, token);
+
+        IMFMediaEventQueue_QueueEventParamUnk(stream->event_queue, MEMediaSample, &GUID_NULL, S_OK, (IUnknown *)sample);
+        IMFSample_Release(sample);
+    }
+
+    g_object_get(stream->appsink, "eos", &stream->eos, NULL);
+    if (stream->eos)
+    {
+        if (token)
+            IUnknown_Release(token);
+        IMFMediaEventQueue_QueueEventParamVar(stream->event_queue, MEEndOfStream, &GUID_NULL, S_OK, &empty_var);
+        dispatch_end_of_presentation(stream->parent_source);
+    }
+}
+
 static HRESULT WINAPI source_async_commands_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result)
 {
     struct media_source *source = impl_from_async_commands_callback_IMFAsyncCallback(iface);
@@ -360,6 +419,9 @@ static HRESULT WINAPI source_async_commands_Invoke(IMFAsyncCallback *iface, IMFA
         case SOURCE_ASYNC_START:
             start_pipeline(source, command);
             break;
+        case SOURCE_ASYNC_REQUEST_SAMPLE:
+            wait_on_sample(command->u.request_sample.stream, command->u.request_sample.token);
+            break;
     }
 
     IUnknown_Release(state);
@@ -647,13 +709,37 @@ static HRESULT WINAPI media_stream_GetStreamDescriptor(IMFMediaStream* iface, IM
 static HRESULT WINAPI media_stream_RequestSample(IMFMediaStream *iface, IUnknown *token)
 {
     struct media_stream *stream = impl_from_IMFMediaStream(iface);
+    struct source_async_command *command;
+    HRESULT hr;
 
     TRACE("(%p)->(%p)\n", iface, token);
 
     if (stream->state == STREAM_SHUTDOWN)
         return MF_E_SHUTDOWN;
 
-    return E_NOTIMPL;
+    if (stream->state == STREAM_INACTIVE)
+    {
+        WARN("Stream isn't active\n");
+        return MF_E_MEDIA_SOURCE_WRONGSTATE;
+    }
+
+    if (stream->eos)
+    {
+        return MF_E_END_OF_STREAM;
+    }
+
+    if (SUCCEEDED(hr = source_create_async_op(SOURCE_ASYNC_REQUEST_SAMPLE, &command)))
+    {
+        command->u.request_sample.stream = stream;
+        if (token)
+            IUnknown_AddRef(token);
+        command->u.request_sample.token = token;
+
+        /* Once pause support is added, this will need to put into a stream queue, and synchronization will need to be added*/
+        hr = MFPutWorkItem(stream->parent_source->async_commands_queue, &stream->parent_source->async_commands_callback, &command->IUnknown_iface);
+    }
+
+    return hr;
 }
 
 static const IMFMediaStreamVtbl media_stream_vtbl =
@@ -738,6 +824,7 @@ static HRESULT new_media_stream(struct media_source *source, GstPad *pad, DWORD
     object->stream_id = stream_id;
 
     object->state = STREAM_INACTIVE;
+    object->eos = FALSE;
 
     if (FAILED(hr = MFCreateEventQueue(&object->event_queue)))
         goto fail;
diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c
index 9aa17ad00ab..f28ef8f117c 100644
--- a/dlls/winegstreamer/mfplat.c
+++ b/dlls/winegstreamer/mfplat.c
@@ -750,3 +750,72 @@ GstCaps *caps_from_mf_media_type(IMFMediaType *type)
     FIXME("Unrecognized major type %s\n", debugstr_guid(&major_type));
     return NULL;
 }
+
+/* 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)
+{
+    IMFMediaBuffer *mf_buffer = NULL;
+    GstMapInfo map_info = {0};
+    LONGLONG duration, time;
+    BYTE *mapped_buf = NULL;
+    IMFSample *out = NULL;
+    HRESULT hr;
+
+    if (FAILED(hr = MFCreateSample(&out)))
+        goto done;
+
+    duration = GST_BUFFER_DURATION(gst_buffer);
+    time = GST_BUFFER_PTS(gst_buffer);
+
+    if (FAILED(hr = IMFSample_SetSampleDuration(out, duration / 100)))
+        goto done;
+
+    if (FAILED(hr = IMFSample_SetSampleTime(out, time / 100)))
+        goto done;
+
+    if (!gst_buffer_map(gst_buffer, &map_info, GST_MAP_READ))
+    {
+        hr = E_FAIL;
+        goto done;
+    }
+
+    if (FAILED(hr = MFCreateMemoryBuffer(map_info.maxsize, &mf_buffer)))
+        goto done;
+
+    if (FAILED(hr = IMFMediaBuffer_Lock(mf_buffer, &mapped_buf, NULL, NULL)))
+        goto done;
+
+    memcpy(mapped_buf, map_info.data, map_info.size);
+
+    if (FAILED(hr = IMFMediaBuffer_Unlock(mf_buffer)))
+        goto done;
+
+    if (FAILED(hr = IMFMediaBuffer_SetCurrentLength(mf_buffer, map_info.size)))
+        goto done;
+
+    if (FAILED(hr = IMFSample_AddBuffer(out, mf_buffer)))
+        goto done;
+
+done:
+    if (mf_buffer)
+        IMFMediaBuffer_Release(mf_buffer);
+    if (map_info.data)
+        gst_buffer_unmap(gst_buffer, &map_info);
+    if (FAILED(hr))
+    {
+        ERR("Failed to copy IMFSample to GstBuffer, hr = %#x\n", hr);
+        if (out)
+            IMFSample_Release(out);
+        out = NULL;
+    }
+
+    return out;
+}
-- 
2.28.0




More information about the wine-devel mailing list