[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