On 10/6/20 10:59 AM, Derek Lesho wrote:
Signed-off-by: Derek Lesho dlesho@codeweavers.com
v2:
- Squashed prior commit for IMFMediaType conversion in.
- Replaced usage of gst_bus_poll with better (not perfect) alternative.
- Addressed comments.
dlls/mfplat/tests/mfplat.c | 8 +- dlls/winegstreamer/gst_private.h | 1 + dlls/winegstreamer/media_source.c | 316 +++++++++++++++++++++++++++++- dlls/winegstreamer/mfplat.c | 149 ++++++++++++++ 4 files changed, 466 insertions(+), 8 deletions(-)
diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c index 15d5bcba3d6..bffce2bc114 100644 --- a/dlls/mfplat/tests/mfplat.c +++ b/dlls/mfplat/tests/mfplat.c @@ -605,10 +605,7 @@ todo_wine
var.vt = VT_EMPTY; hr = IMFMediaSource_Start(mediasource, descriptor, &GUID_NULL, &var);
-todo_wine ok(hr == S_OK, "Failed to start media source, hr %#x.\n", hr);
if (FAILED(hr))
goto skip_source_tests;
get_event((IMFMediaEventGenerator *)mediasource, MENewStream, &var); ok(var.vt == VT_UNKNOWN, "Unexpected value type %u from MENewStream event.\n", var.vt);
@@ -626,10 +623,13 @@ 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) {
@@ -667,11 +667,11 @@ todo_wine
hr = IMFMediaStream_RequestSample(video_stream, NULL); ok(hr == MF_E_END_OF_STREAM, "Unexpected hr %#x.\n", hr);
IMFMediaStream_Release(video_stream);
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 60b38a48f5a..07556802a51 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)
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;
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 c70de184f0b..5eb4465fea6 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -55,14 +55,39 @@ struct media_stream { STREAM_INACTIVE, STREAM_SHUTDOWN,
} state; DWORD stream_id;STREAM_RUNNING,
- BOOL eos;
+};
+enum source_async_op +{
- SOURCE_ASYNC_START,
+};
+struct source_async_command +{
- IUnknown IUnknown_iface;
- LONG refcount;
- enum source_async_op op;
- union
- {
struct
{
IMFPresentationDescriptor *descriptor;
GUID format;
PROPVARIANT position;
} start;
- } u;
};
struct media_source { IMFMediaSource IMFMediaSource_iface;
- IMFAsyncCallback async_commands_callback; LONG ref;
- DWORD async_commands_queue; IMFMediaEventQueue *event_queue; IMFByteStream *byte_stream; struct media_stream **streams;
@@ -76,6 +101,7 @@ struct media_source { SOURCE_OPENING, SOURCE_STOPPED,
} state; HANDLE no_more_pads_event;SOURCE_RUNNING, SOURCE_SHUTDOWN,
@@ -91,7 +117,266 @@ static inline struct media_source *impl_from_IMFMediaSource(IMFMediaSource *ifac return CONTAINING_RECORD(iface, struct media_source, IMFMediaSource_iface); }
-static GstFlowReturn bytestream_wrapper_pull(GstPad *pad, GstObject *parent, guint64 ofs, guint len, +static inline struct media_source *impl_from_async_commands_callback_IMFAsyncCallback(IMFAsyncCallback *iface) +{
- return CONTAINING_RECORD(iface, struct media_source, async_commands_callback);
+}
+static inline struct source_async_command *impl_from_async_command_IUnknown(IUnknown *iface) +{
- return CONTAINING_RECORD(iface, struct source_async_command, IUnknown_iface);
+}
+static HRESULT WINAPI source_async_command_QueryInterface(IUnknown *iface, REFIID riid, void **obj) +{
- if (IsEqualIID(riid, &IID_IUnknown))
- {
*obj = iface;
IUnknown_AddRef(iface);
return S_OK;
- }
- WARN("Unsupported interface %s.\n", debugstr_guid(riid));
- *obj = NULL;
- return E_NOINTERFACE;
+}
+static ULONG WINAPI source_async_command_AddRef(IUnknown *iface) +{
- struct source_async_command *command = impl_from_async_command_IUnknown(iface);
- return InterlockedIncrement(&command->refcount);
+}
+static ULONG WINAPI source_async_command_Release(IUnknown *iface) +{
- struct source_async_command *command = impl_from_async_command_IUnknown(iface);
- ULONG refcount = InterlockedDecrement(&command->refcount);
- if (!refcount)
- {
if (command->op == SOURCE_ASYNC_START)
PropVariantClear(&command->u.start.position);
heap_free(command);
- }
- return refcount;
+}
+static const IUnknownVtbl source_async_command_vtbl = +{
- source_async_command_QueryInterface,
- source_async_command_AddRef,
- source_async_command_Release,
+};
+static HRESULT source_create_async_op(enum source_async_op op, struct source_async_command **ret) +{
- struct source_async_command *command;
- if (!(command = heap_alloc_zero(sizeof(*command))))
return E_OUTOFMEMORY;
- command->IUnknown_iface.lpVtbl = &source_async_command_vtbl;
- command->op = op;
- *ret = command;
- return S_OK;
+}
+static HRESULT WINAPI callback_QueryInterface(IMFAsyncCallback *iface, REFIID riid, void **obj) +{
- TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj);
- if (IsEqualIID(riid, &IID_IMFAsyncCallback) ||
IsEqualIID(riid, &IID_IUnknown))
- {
*obj = iface;
IMFAsyncCallback_AddRef(iface);
return S_OK;
- }
- WARN("Unsupported %s.\n", debugstr_guid(riid));
- *obj = NULL;
- return E_NOINTERFACE;
+}
+static HRESULT WINAPI callback_GetParameters(IMFAsyncCallback *iface,
DWORD *flags, DWORD *queue)
+{
- return E_NOTIMPL;
+}
+static ULONG WINAPI source_async_commands_callback_AddRef(IMFAsyncCallback *iface) +{
- struct media_source *source = impl_from_async_commands_callback_IMFAsyncCallback(iface);
- return IMFMediaSource_AddRef(&source->IMFMediaSource_iface);
+}
+static ULONG WINAPI source_async_commands_callback_Release(IMFAsyncCallback *iface) +{
- struct media_source *source = impl_from_async_commands_callback_IMFAsyncCallback(iface);
- return IMFMediaSource_Release(&source->IMFMediaSource_iface);
+}
+static IMFStreamDescriptor *stream_descriptor_from_id(IMFPresentationDescriptor *pres_desc, DWORD id, BOOL *selected) +{
- ULONG sd_count;
- IMFStreamDescriptor *ret;
- unsigned int i;
- if (FAILED(IMFPresentationDescriptor_GetStreamDescriptorCount(pres_desc, &sd_count)))
return NULL;
- for (i = 0; i < sd_count; i++)
- {
DWORD stream_id;
if (FAILED(IMFPresentationDescriptor_GetStreamDescriptorByIndex(pres_desc, i, selected, &ret)))
return NULL;
if (SUCCEEDED(IMFStreamDescriptor_GetStreamIdentifier(ret, &stream_id)) && stream_id == id)
return ret;
IMFStreamDescriptor_Release(ret);
- }
- return NULL;
+}
+static void start_pipeline(struct media_source *source, struct source_async_command *command) +{
- PROPVARIANT *position = &command->u.start.position;
- BOOL seek_message = source->state != SOURCE_STOPPED && position->vt != VT_EMPTY;
- GstStateChangeReturn ret;
- unsigned int i;
- gst_element_set_state(source->container, GST_STATE_PAUSED);
- ret = gst_element_get_state(source->container, NULL, NULL, -1);
- assert(ret == GST_STATE_CHANGE_SUCCESS);
- /* seek to beginning on stop->play */
- if (source->state == SOURCE_STOPPED && position->vt == VT_EMPTY)
- {
position->vt = VT_I8;
position->u.hVal.QuadPart = 0;
- }
- for (i = 0; i < source->stream_count; i++)
- {
struct media_stream *stream;
IMFStreamDescriptor *sd;
IMFMediaTypeHandler *mth;
IMFMediaType *current_mt;
GstCaps *current_caps;
GstCaps *prev_caps;
DWORD stream_id;
BOOL was_active;
BOOL selected;
stream = source->streams[i];
IMFStreamDescriptor_GetStreamIdentifier(stream->descriptor, &stream_id);
sd = stream_descriptor_from_id(command->u.start.descriptor, stream_id, &selected);
IMFStreamDescriptor_Release(sd);
was_active = stream->state != STREAM_INACTIVE;
stream->state = selected ? STREAM_RUNNING : STREAM_INACTIVE;
if (selected)
{
IMFStreamDescriptor_GetMediaTypeHandler(stream->descriptor, &mth);
IMFMediaTypeHandler_GetCurrentMediaType(mth, ¤t_mt);
current_caps = caps_from_mf_media_type(current_mt);
g_object_get(stream->appsink, "caps", &prev_caps, NULL);
if (!gst_caps_is_equal(prev_caps, current_caps))
{
GstEvent *reconfigure_event = gst_event_new_reconfigure();
g_object_set(stream->appsink, "caps", current_caps, NULL);
gst_pad_push_event(gst_element_get_static_pad(stream->appsink, "sink"), reconfigure_event);
}
gst_caps_unref(current_caps);
gst_caps_unref(prev_caps);
IMFMediaType_Release(current_mt);
IMFMediaTypeHandler_Release(mth);
}
if (position->vt != VT_EMPTY)
{
GstEvent *seek_event = gst_event_new_seek(1.0, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH,
GST_SEEK_TYPE_SET, position->u.hVal.QuadPart / 100, GST_SEEK_TYPE_NONE, 0);
GstSample *preroll;
gst_pad_push_event(stream->my_sink, seek_event);
/* this works because the pre-seek preroll is already removed by media_source_constructor */
g_signal_emit_by_name(stream->appsink, "pull-preroll", &preroll);
if (preroll)
gst_sample_unref(preroll);
What is the point of doing this?
stream->eos = FALSE;
}
if (selected)
{
TRACE("Stream %u (%p) selected\n", i, stream);
IMFMediaEventQueue_QueueEventParamUnk(source->event_queue,
was_active ? MEUpdatedStream : MENewStream, &GUID_NULL,
S_OK, (IUnknown*) &stream->IMFMediaStream_iface);
Please apply at least some indentation to line continuations.
IMFMediaEventQueue_QueueEventParamVar(stream->event_queue,
seek_message ? MEStreamSeeked : MEStreamStarted, &GUID_NULL, S_OK, position);
}
- }
- IMFMediaEventQueue_QueueEventParamVar(source->event_queue,
seek_message ? MESourceSeeked : MESourceStarted,
&GUID_NULL, S_OK, position);
- source->state = SOURCE_RUNNING;
- gst_element_set_state(source->container, GST_STATE_PLAYING);
- gst_element_get_state(source->container, NULL, NULL, -1);
And if this method is asynchronous, do you need this gst_element_get_state() call?
+}
+static HRESULT WINAPI source_async_commands_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result) +{
- struct media_source *source = impl_from_async_commands_callback_IMFAsyncCallback(iface);
- struct source_async_command *command;
- IUnknown *state;
- HRESULT hr;
- if (source->state == SOURCE_SHUTDOWN)
return S_OK;
- if (FAILED(hr = IMFAsyncResult_GetState(result, &state)))
return hr;
- command = impl_from_async_command_IUnknown(state);
- switch (command->op)
- {
case SOURCE_ASYNC_START:
start_pipeline(source, command);
break;
- }
- IUnknown_Release(state);
- return S_OK;
+}
+static const IMFAsyncCallbackVtbl source_async_commands_callback_vtbl = +{
- callback_QueryInterface,
- source_async_commands_callback_AddRef,
- source_async_commands_callback_Release,
- callback_GetParameters,
- source_async_commands_Invoke,
+};
+GstFlowReturn bytestream_wrapper_pull(GstPad *pad, GstObject *parent, guint64 ofs, guint len, GstBuffer **buf) { struct media_source *source = gst_pad_get_element_private(pad); @@ -432,6 +717,8 @@ static HRESULT media_stream_connect_to_sink(struct media_stream *stream) if (gst_pad_link(stream->their_src, stream->my_sink) != GST_PAD_LINK_OK) return E_FAIL;
- g_object_set(stream->appsink, "caps", source_caps, NULL);
Again, what's the point of this?
return S_OK;
}
@@ -682,16 +969,30 @@ static HRESULT WINAPI media_source_CreatePresentationDescriptor(IMFMediaSource * }
static HRESULT WINAPI media_source_Start(IMFMediaSource *iface, IMFPresentationDescriptor *descriptor,
const GUID *time_format, const PROPVARIANT *start_position)
const GUID *time_format, const PROPVARIANT *position)
{ struct media_source *source = impl_from_IMFMediaSource(iface);
- struct source_async_command *command;
- HRESULT hr;
- FIXME("(%p)->(%p, %p, %p): stub\n", source, descriptor, time_format, start_position);
TRACE("(%p)->(%p, %p, %p)\n", source, descriptor, time_format, position);
if (source->state == SOURCE_SHUTDOWN) return MF_E_SHUTDOWN;
- return E_NOTIMPL;
- if (!(IsEqualIID(time_format, &GUID_NULL)))
return MF_E_UNSUPPORTED_TIME_FORMAT;
- if (SUCCEEDED(hr = source_create_async_op(SOURCE_ASYNC_START, &command)))
- {
command->u.start.descriptor = descriptor;
command->u.start.format = *time_format;
PropVariantCopy(&command->u.start.position, position);
hr = MFPutWorkItem(source->async_commands_queue, &source->async_commands_callback, &command->IUnknown_iface);
- }
There are likely mfplat pecularities I'm not aware of here, but does this need to be asynchronous? I know that the documentation says "this method is asynchronous", but it's not immediately obvious to me that there isn't already a level of asynchronicity between this call and any of its effects.
- return hr;
}
static HRESULT WINAPI media_source_Stop(IMFMediaSource *iface) @@ -772,6 +1073,9 @@ static HRESULT WINAPI media_source_Shutdown(IMFMediaSource *iface) if (source->no_more_pads_event) CloseHandle(source->no_more_pads_event);
- if (source->async_commands_queue)
MFUnlockWorkQueue(source->async_commands_queue);
- return S_OK;
}
@@ -852,6 +1156,7 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, struct media_ return E_OUTOFMEMORY;
object->IMFMediaSource_iface.lpVtbl = &IMFMediaSource_vtbl;
- object->async_commands_callback.lpVtbl = &source_async_commands_callback_vtbl; object->ref = 1; object->byte_stream = bytestream; IMFByteStream_AddRef(bytestream);
@@ -860,6 +1165,9 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, struct media_ if (FAILED(hr = MFCreateEventQueue(&object->event_queue))) goto fail;
- if (FAILED(hr = MFAllocateWorkQueue(&object->async_commands_queue)))
goto fail;
- object->container = gst_bin_new(NULL); object->bus = gst_bus_new(); gst_bus_set_sync_handler(object->bus, mf_src_bus_watch_wrapper, object, NULL);
diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c index 2e8b0978648..9aa17ad00ab 100644 --- a/dlls/winegstreamer/mfplat.c +++ b/dlls/winegstreamer/mfplat.c @@ -601,3 +601,152 @@ IMFMediaType *mf_media_type_from_caps(const GstCaps *caps)
return media_type;
}
+GstCaps *caps_from_mf_media_type(IMFMediaType *type) +{
- GUID major_type;
- GUID subtype;
- GstCaps *output = NULL;
- if (FAILED(IMFMediaType_GetMajorType(type, &major_type)))
return NULL;
- if (FAILED(IMFMediaType_GetGUID(type, &MF_MT_SUBTYPE, &subtype)))
return NULL;
- if (IsEqualGUID(&major_type, &MFMediaType_Video))
- {
UINT64 frame_rate = 0, frame_size = 0;
DWORD width, height, framerate_num, framerate_den;
UINT32 unused;
if (FAILED(IMFMediaType_GetUINT64(type, &MF_MT_FRAME_SIZE, &frame_size)))
return NULL;
width = frame_size >> 32;
height = frame_size;
if (FAILED(IMFMediaType_GetUINT64(type, &MF_MT_FRAME_RATE, &frame_rate)))
{
frame_rate = TRUE;
framerate_num = 0;
framerate_den = 1;
}
else
{
framerate_num = frame_rate >> 32;
framerate_den = frame_rate;
}
/* Check if type is uncompressed */
if (SUCCEEDED(MFCalculateImageSize(&subtype, 100, 100, &unused)))
Early return could save a level of indentation.
I also feel like there's an easier way to do this check. Maybe something like:
for (i = 0; i < ARRAY_SIZE(uncompressed_video_formats); i++) { ... }
if (format == GST_VIDEO_FORMAT_UNKNOWN && !memcmp(&subtype, &MFVideoFormat_Base.Data2, ...)) format = gst_video_format_from_fourcc(subtype.Data1);
if (format == GST_VIDEO_FORMAT_UNKNOWN) { FIXME("Unrecognized subtype %s.\n", debugstr_guid(&subtype)); return NULL; }
{
GstVideoFormat format = GST_VIDEO_FORMAT_UNKNOWN;
unsigned int i;
output = gst_caps_new_empty_simple("video/x-raw");
for (i = 0; i < ARRAY_SIZE(uncompressed_video_formats); i++)
{
if (IsEqualGUID(uncompressed_video_formats[i].subtype, &subtype))
{
format = uncompressed_video_formats[i].format;
break;
}
}
if (format == GST_VIDEO_FORMAT_UNKNOWN)
{
format = gst_video_format_from_fourcc(subtype.Data1);
}
if (format == GST_VIDEO_FORMAT_UNKNOWN)
{
FIXME("Unrecognized format %s\n", debugstr_guid(&subtype));
return NULL;
}
else
This "else" is redundant.
{
GstVideoInfo info;
gst_video_info_set_format(&info, format, width, height);
output = gst_video_info_to_caps(&info);
}
}
else {
Inconsistent braces.
FIXME("Unrecognized subtype %s\n", debugstr_guid(&subtype));
return NULL;
}
if (frame_size)
{
gst_caps_set_simple(output, "width", G_TYPE_INT, width, NULL);
gst_caps_set_simple(output, "height", G_TYPE_INT, height, NULL);
}
if (frame_rate)
Why do you need to check this is nonzero? Can the frame rate really be 0/0?
For that matter, do we need to give GStreamer the framerate at all? It should only ever matter to sink elements (or those that respect a clock).
gst_caps_set_simple(output, "framerate", GST_TYPE_FRACTION, framerate_num, framerate_den, NULL);
return output;
- }
- else if (IsEqualGUID(&major_type, &MFMediaType_Audio))
- {
DWORD rate, channels, channel_mask, bitrate;
if (IsEqualGUID(&subtype, &MFAudioFormat_Float))
{
output = gst_caps_new_empty_simple("audio/x-raw");
gst_caps_set_simple(output, "format", G_TYPE_STRING, "F32LE", NULL);
gst_caps_set_simple(output, "layout", G_TYPE_STRING, "interleaved", NULL);
}
else if (IsEqualGUID(&subtype, &MFAudioFormat_PCM))
{
DWORD bits_per_sample;
if (SUCCEEDED(IMFMediaType_GetUINT32(type, &MF_MT_AUDIO_BITS_PER_SAMPLE, &bits_per_sample)))
{
char format[6];
char type;
type = bits_per_sample > 8 ? 'S' : 'U';
output = gst_caps_new_empty_simple("audio/x-raw");
sprintf(format, "%c%u%s", type, bits_per_sample, bits_per_sample > 8 ? "LE" : "");
gst_caps_set_simple(output, "format", G_TYPE_STRING, format, NULL);
}
else
{
ERR("Bits per sample not set.\n");
return NULL;
}
}
else
{
FIXME("Unrecognized subtype %s\n", debugstr_guid(&subtype));
return NULL;
}
if (SUCCEEDED(IMFMediaType_GetUINT32(type, &MF_MT_AUDIO_SAMPLES_PER_SECOND, &rate)))
{
gst_caps_set_simple(output, "rate", G_TYPE_INT, rate, NULL);
}
if (SUCCEEDED(IMFMediaType_GetUINT32(type, &MF_MT_AUDIO_NUM_CHANNELS, &channels)))
{
gst_caps_set_simple(output, "channels", G_TYPE_INT, channels, NULL);
}
if (SUCCEEDED(IMFMediaType_GetUINT32(type, &MF_MT_AUDIO_CHANNEL_MASK, &channel_mask)))
{
gst_caps_set_simple(output, "channel-mask", GST_TYPE_BITMASK, (guint64) channel_mask, NULL);
}
if (SUCCEEDED(IMFMediaType_GetUINT32(type, &MF_MT_AVG_BITRATE, &bitrate)))
{
gst_caps_set_simple(output, "bitrate", G_TYPE_INT, bitrate, NULL);
}
return output;
- }
- FIXME("Unrecognized major type %s\n", debugstr_guid(&major_type));
- return NULL;
+}