[PATCH 5/5] winegstreamer: Implement IMFMediaStream::RequestSample.
Derek Lesho
dlesho at codeweavers.com
Tue Oct 27 11:12:08 CDT 2020
Signed-off-by: Derek Lesho <dlesho at codeweavers.com>
---
v7: Updated mfreadwrite tests.
---
dlls/mfplat/tests/mfplat.c | 4 --
dlls/mfreadwrite/tests/mfplat.c | 4 +-
dlls/winegstreamer/gst_private.h | 1 +
dlls/winegstreamer/media_source.c | 92 ++++++++++++++++++++++++++++++-
dlls/winegstreamer/mfplat.c | 69 +++++++++++++++++++++++
5 files changed, 164 insertions(+), 6 deletions(-)
diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c
index a080035e477..8f9fb3faff3 100644
--- a/dlls/mfplat/tests/mfplat.c
+++ b/dlls/mfplat/tests/mfplat.c
@@ -621,13 +621,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)
{
@@ -668,7 +665,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/mfreadwrite/tests/mfplat.c b/dlls/mfreadwrite/tests/mfplat.c
index cfe68cb6736..a44f7ca55d0 100644
--- a/dlls/mfreadwrite/tests/mfplat.c
+++ b/dlls/mfreadwrite/tests/mfplat.c
@@ -722,7 +722,6 @@ static void test_source_reader(void)
hr = IMFSourceReader_ReadSample(reader, MF_SOURCE_READER_FIRST_AUDIO_STREAM, 0, &actual_index, &stream_flags,
×tamp, &sample);
-todo_wine
ok(hr == S_OK, "Failed to get a sample, hr %#x.\n", hr);
if (hr != S_OK)
goto skip_read_sample;
@@ -748,8 +747,11 @@ todo_wine
×tamp, &sample);
ok(hr == S_OK, "Failed to get a sample, hr %#x.\n", hr);
ok(actual_index == 0, "Unexpected stream index %u\n", actual_index);
+todo_wine
+{
ok(stream_flags == MF_SOURCE_READERF_ENDOFSTREAM, "Unexpected stream flags %#x.\n", stream_flags);
ok(!sample, "Unexpected sample object.\n");
+}
hr = IMFSourceReader_ReadSample(reader, MF_SOURCE_READER_FIRST_AUDIO_STREAM, MF_SOURCE_READER_CONTROLF_DRAIN,
&actual_index, &stream_flags, ×tamp, &sample);
diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h
index 3a8020b6760..28e424439d8 100644
--- a/dlls/winegstreamer/gst_private.h
+++ b/dlls/winegstreamer/gst_private.h
@@ -80,6 +80,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 9c6e7bfd539..ef694bf194a 100644
--- a/dlls/winegstreamer/media_source.c
+++ b/dlls/winegstreamer/media_source.c
@@ -58,11 +58,13 @@ struct media_stream
STREAM_RUNNING,
} state;
DWORD stream_id;
+ BOOL eos;
};
enum source_async_op
{
SOURCE_ASYNC_START,
+ SOURCE_ASYNC_REQUEST_SAMPLE,
};
struct source_async_command
@@ -78,6 +80,11 @@ struct source_async_command
GUID format;
PROPVARIANT position;
} start;
+ struct
+ {
+ struct media_stream *stream;
+ IUnknown *token;
+ } request_sample;
} u;
};
@@ -311,6 +318,8 @@ static void start_pipeline(struct media_source *source, struct source_async_comm
GST_SEEK_TYPE_SET, position->u.hVal.QuadPart / 100, GST_SEEK_TYPE_NONE, 0);
gst_pad_push_event(stream->my_sink, seek_event);
+
+ stream->eos = FALSE;
}
if (selected)
@@ -334,6 +343,59 @@ static void start_pipeline(struct media_source *source, struct source_async_comm
gst_element_set_state(source->container, GST_STATE_PLAYING);
}
+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);
@@ -353,6 +415,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);
@@ -640,13 +705,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 =
@@ -729,6 +818,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 46bbb2ac2b1..908f3d83ef9 100644
--- a/dlls/winegstreamer/mfplat.c
+++ b/dlls/winegstreamer/mfplat.c
@@ -731,3 +731,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