On 9/9/20 7:00 PM, Zebediah Figura wrote:
On 9/8/20 10:47 AM, Derek Lesho wrote:
Signed-off-by: Derek Lesho dlesho@codeweavers.com
dlls/winegstreamer/gst_cbs.c | 13 ++ dlls/winegstreamer/gst_cbs.h | 8 ++ dlls/winegstreamer/gst_private.h | 5 + dlls/winegstreamer/media_source.c | 137 +++++++++++++++++++- dlls/winegstreamer/mfplat.c | 208 ++++++++++++++++++++++++++++++ 5 files changed, 368 insertions(+), 3 deletions(-)
diff --git a/dlls/winegstreamer/gst_cbs.c b/dlls/winegstreamer/gst_cbs.c index 4755f5b42f1..e56c987eb38 100644 --- a/dlls/winegstreamer/gst_cbs.c +++ b/dlls/winegstreamer/gst_cbs.c @@ -404,3 +404,16 @@ void source_all_streams_wrapper(GstElement *element, gpointer user)
call_cb(&cbdata);
}
+GstPadProbeReturn caps_listener_wrapper(GstPad *pad, GstPadProbeInfo *info, gpointer user) +{
- struct cb_data cbdata = { STREAM_PAD_EVENT };
- cbdata.u.pad_probe_data.pad = pad;
- cbdata.u.pad_probe_data.info = info;
- cbdata.u.pad_probe_data.user = user;
- call_cb(&cbdata);
- return cbdata.u.pad_probe_data.ret;
+} diff --git a/dlls/winegstreamer/gst_cbs.h b/dlls/winegstreamer/gst_cbs.h index d87cc8c21e9..7173c09746e 100644 --- a/dlls/winegstreamer/gst_cbs.h +++ b/dlls/winegstreamer/gst_cbs.h @@ -52,6 +52,7 @@ enum CB_TYPE { SOURCE_STREAM_ADDED, SOURCE_STREAM_REMOVED, SOURCE_ALL_STREAMS,
- STREAM_PAD_EVENT, MEDIA_SOURCE_MAX, };
@@ -137,6 +138,12 @@ struct cb_data { GstQuery *query; gboolean ret; } query_sink_data;
struct pad_probe_data {
GstPad *pad;
GstPadProbeInfo *info;
gpointer user;
GstPadProbeReturn ret;
} pad_probe_data; } u; int finished;
@@ -172,5 +179,6 @@ GstBusSyncReply watch_source_bus_wrapper(GstBus *bus, GstMessage *message, gpoin void source_stream_added_wrapper(GstElement *bin, GstPad *pad, gpointer user) DECLSPEC_HIDDEN; void source_stream_removed_wrapper(GstElement *element, GstPad *pad, gpointer user) DECLSPEC_HIDDEN; void source_all_streams_wrapper(GstElement *element, gpointer user) DECLSPEC_HIDDEN; +GstPadProbeReturn caps_listener_wrapper(GstPad *pad, GstPadProbeInfo *info, gpointer user);
#endif diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index ef07d3591e7..86392eea4e3 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -36,6 +36,7 @@ #include "winuser.h" #include "dshow.h" #include "strmif.h" +#include "mfobjects.h" #include "wine/heap.h" #include "wine/strmbase.h"
@@ -54,6 +55,10 @@ void start_dispatch_thread(void) DECLSPEC_HIDDEN;
extern HRESULT mfplat_get_class_object(REFCLSID rclsid, REFIID riid, void **obj) DECLSPEC_HIDDEN;
+HRESULT winegstreamer_stream_handler_create(REFIID riid, void **obj) DECLSPEC_HIDDEN; +GstCaps *make_mf_compatible_caps(GstCaps *caps); +IMFMediaType *mf_media_type_from_caps(GstCaps *caps);
Missing DECLSPEC_HIDDEN.
👌
HRESULT winegstreamer_stream_handler_create(REFIID riid, void **obj) DECLSPEC_HIDDEN;
#endif /* __GST_PRIVATE_INCLUDED__ */
diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index 29af2b72def..345b1fe4528 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -47,14 +47,19 @@ struct media_stream LONG ref; struct media_source *parent_source; IMFMediaEventQueue *event_queue;
IMFStreamDescriptor *descriptor; GstElement *appsink; GstPad *their_src, *my_sink;
GstCaps *their_caps; enum { STREAM_STUB, STREAM_INACTIVE, STREAM_SHUTDOWN, } state;
/* used when in STUB state: */
DWORD stream_id;
HANDLE caps_event; };
struct media_source
@@ -312,6 +317,8 @@ static ULONG WINAPI media_stream_Release(IMFMediaStream *iface) { if (stream->my_sink) gst_object_unref(GST_OBJECT(stream->my_sink));
if (stream->descriptor)
IMFStreamDescriptor_Release(stream->descriptor); if (stream->event_queue) IMFMediaEventQueue_Release(stream->event_queue); if (stream->parent_source)
@@ -393,7 +400,10 @@ static HRESULT WINAPI media_stream_GetStreamDescriptor(IMFMediaStream* iface, IM if (stream->state == STREAM_SHUTDOWN) return MF_E_SHUTDOWN;
- return E_NOTIMPL;
IMFStreamDescriptor_AddRef(stream->descriptor);
*descriptor = stream->descriptor;
return S_OK; }
static HRESULT WINAPI media_stream_RequestSample(IMFMediaStream *iface, IUnknown *token)
@@ -436,9 +446,12 @@ static HRESULT new_media_stream(struct media_source *source, GstPad *pad, DWORD IMFMediaSource_AddRef(&source->IMFMediaSource_iface); object->parent_source = source; object->their_src = pad;
object->stream_id = stream_id;
object->state = STREAM_STUB;
object->caps_event = CreateEventA(NULL, TRUE, FALSE, NULL);
if (FAILED(hr = MFCreateEventQueue(&object->event_queue))) goto fail;
@@ -455,10 +468,10 @@ static HRESULT new_media_stream(struct media_source *source, GstPad *pad, DWORD g_object_set(object->appsink, "wait-on-eos", FALSE, NULL);
object->my_sink = gst_element_get_static_pad(object->appsink, "sink");
- gst_pad_set_element_private(object->my_sink, object);
gst_pad_link(object->their_src, object->my_sink);
gst_pad_add_probe(object->my_sink, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, caps_listener_wrapper, object, NULL);
gst_element_sync_state_with_parent(object->appsink); TRACE("->(%p)\n", object);
@@ -473,6 +486,43 @@ static HRESULT new_media_stream(struct media_source *source, GstPad *pad, DWORD return hr; }
+static HRESULT media_stream_init_desc(struct media_stream *stream) +{
- HRESULT hr;
- IMFMediaTypeHandler *type_handler;
- IMFMediaType *stream_type = NULL;
- stream->their_caps = gst_caps_fixate(stream->their_caps);
I think this is unnecessary; the caps should already be fixed.
👌
- stream_type = mf_media_type_from_caps(stream->their_caps);
- gst_caps_unref(stream->their_caps);
This seems a bit error-prone. I'd suggest getting rid of the field and instead calling gst_caps_copy(gst_pad_get_current_caps(...))
Can we be sure that the caps will be set after the pad probe callback is complete?
- if (!stream_type)
- {
hr = E_FAIL;
goto fail;
- }
- if (FAILED(hr = MFCreateStreamDescriptor(stream->stream_id, 1, &stream_type, &stream->descriptor)))
goto fail;
- if (FAILED(hr = IMFStreamDescriptor_GetMediaTypeHandler(stream->descriptor, &type_handler)))
goto fail;
- if (FAILED(hr = IMFMediaTypeHandler_SetCurrentMediaType(type_handler, stream_type)))
goto fail;
- IMFMediaTypeHandler_Release(type_handler);
- stream->state = STREAM_INACTIVE;
- return S_OK;
- fail:
- ERR("media stream initialization failed with %x\n", hr);
While this does tell you that one of the mfplat functions failed, it doesn't actually tell you which one. There's similar patterns elsewhere in this series.
Is that a problem? The likelyhood of one of these functions failing is very slim, and in that case, you could use use +mfplat to see the last function to run before the error. If conversion to a media type failed, the relevant conversion function will also emit an error.
- if (type_handler)
IMFMediaTypeHandler_Release(type_handler);
- return hr;
+}
- static HRESULT WINAPI media_source_QueryInterface(IMFMediaSource *iface, REFIID riid, void **out) { struct media_source *source = impl_from_IMFMediaSource(iface);
@@ -680,6 +730,23 @@ static const IMFMediaSourceVtbl IMFMediaSource_vtbl = media_source_Shutdown, };
+/* If this callback is extended to use any significant win32 APIs, a wrapper function
- should be added */
+gboolean stream_found(GstElement *bin, GstPad *pad, GstCaps *caps, gpointer user) +{
- GstCaps *target_caps;
- /* if the stream can be converted into an MF compatible type, we'll go that route
otherwise, we'll rely on decodebin for the whole process */
I think we should leave this part out for now.
I disagree, without it, a lot of compressed media type conversion code becomes dead, and this codepath which this triggers is closer to the windows one.
- if ((target_caps = make_mf_compatible_caps(caps)))
- {
gst_caps_unref(target_caps);
return FALSE;
- }
- return TRUE;
+}
- static void source_stream_added(GstElement *element, GstPad *pad, gpointer user) { struct media_source *source = (struct media_source *) user;
@@ -697,6 +764,36 @@ static void source_stream_added(GstElement *element, GstPad *pad, gpointer user)
TRACE("stream-id: %u\n", stream_id);
- /* This codepath is currently never triggered, as we don't need to ever restart the gstreamer pipeline. It is retained in
case this becomes necessary in the future, for example in a case where different media types require different
post-processing elements. */
In general, if a code path is dead, it shouldn't be introduced (yet).
👌
- for (unsigned int i = 0; i < source->stream_count; i++)
- {
DWORD existing_stream_id;
IMFStreamDescriptor *descriptor = source->streams[i]->descriptor;
if (source->streams[i]->state == STREAM_STUB)
continue;
if (FAILED(IMFStreamDescriptor_GetStreamIdentifier(descriptor, &existing_stream_id)))
goto leave;
if (existing_stream_id == stream_id)
{
struct media_stream *existing_stream = source->streams[i];
GstPadLinkReturn ret;
TRACE("Found existing stream %p\n", existing_stream);
existing_stream->their_src = pad;
if ((ret = gst_pad_link(existing_stream->their_src, existing_stream->my_sink)) != GST_PAD_LINK_OK)
ERR("Error linking decodebin pad to stream %p, err = %d\n", existing_stream, ret);
goto leave;
}
- }
if (FAILED(new_media_stream(source, pad, stream_id, &stream))) { goto leave;
@@ -737,6 +834,26 @@ static void source_all_streams(GstElement *element, gpointer user) SetEvent(source->all_streams_event); }
+static GstPadProbeReturn caps_listener(GstPad *pad, GstPadProbeInfo *info, gpointer user) +{
- struct media_stream *stream = (struct media_stream *) user;
The cast is unnecessary, here and elsewhere.
👌
- GstEvent *event = gst_pad_probe_info_get_event(info);
- if (GST_EVENT_TYPE(event) == GST_EVENT_CAPS)
- {
GstCaps *caps;
TRACE("got caps for stream %p\n", stream);
gst_event_parse_caps(event, &caps);
stream->their_caps = gst_caps_copy(caps);
SetEvent(stream->caps_event);
return GST_PAD_PROBE_REMOVE;
- }
- return GST_PAD_PROBE_OK;
+}
- static HRESULT media_source_constructor(IMFByteStream *bytestream, struct media_source **out_media_source) { GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE(
@@ -787,6 +904,8 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, struct media_
gst_bin_add(GST_BIN(object->container), object->decodebin);
- if(!GetEnvironmentVariableA("MF_DECODE_IN_SOURCE", NULL, 0))
Debugging code?
Yeah, it's useful for finding out whether an issue is caused by the real decoder MFT path, or topology loader. (As it skips all that)
g_signal_connect(object->decodebin, "autoplug-continue", G_CALLBACK(stream_found), object); g_signal_connect(object->decodebin, "pad-added", G_CALLBACK(source_stream_added_wrapper), object); g_signal_connect(object->decodebin, "pad-removed", G_CALLBACK(source_stream_removed_wrapper), object); g_signal_connect(object->decodebin, "no-more-pads", G_CALLBACK(source_all_streams_wrapper), object);
@@ -812,6 +931,12 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, struct media_ }
WaitForSingleObject(object->all_streams_event, INFINITE);
- for (unsigned int i = 0; i < object->stream_count; i++)
Misplaced declaration.
👌
{
WaitForSingleObject(object->streams[i]->caps_event, INFINITE);
if (FAILED(hr = media_stream_init_desc(object->streams[i])))
goto fail;
}
object->state = SOURCE_STOPPED;
@@ -1337,6 +1462,12 @@ void perform_cb_media_source(struct cb_data *cbdata) source_all_streams(data->element, data->user); break; }
- case STREAM_PAD_EVENT:
{
struct pad_probe_data *data = &cbdata->u.pad_probe_data;
cbdata->u.pad_probe_data.ret = caps_listener(data->pad, data->info, data->user);
break;
} default: { ERR("Wrong callback forwarder called\n");
diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c index c996f06211e..3667bc3cc38 100644 --- a/dlls/winegstreamer/mfplat.c +++ b/dlls/winegstreamer/mfplat.c @@ -16,6 +16,11 @@
- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
+#include "config.h" +#include <gst/gst.h>
+#include "gst_private.h"
#include <stdarg.h>
#include "gst_private.h"
@@ -436,3 +441,206 @@ HRESULT mfplat_get_class_object(REFCLSID rclsid, REFIID riid, void **obj)
return CLASS_E_CLASSNOTAVAILABLE;
}
+const static struct
"static const"
👌
+{
- const GUID *subtype;
- GstVideoFormat format;
+} +uncompressed_formats[] =
"uncompressed_video_formats", probably.
👌
+{
- {&MFVideoFormat_ARGB32, GST_VIDEO_FORMAT_BGRA},
- {&MFVideoFormat_RGB32, GST_VIDEO_FORMAT_BGRx},
- {&MFVideoFormat_RGB24, GST_VIDEO_FORMAT_BGR},
- {&MFVideoFormat_RGB565, GST_VIDEO_FORMAT_BGR16},
- {&MFVideoFormat_RGB555, GST_VIDEO_FORMAT_BGR15},
+};
+/* caps will be modified to represent the exact type needed for the format */
This seems wrong in general.
Why?
+static IMFMediaType* transform_to_media_type(GstCaps *caps) +{
- IMFMediaType *media_type;
- GstStructure *info;
- const char *mime_type;
- if (TRACE_ON(mfplat))
- {
gchar *human_readable = gst_caps_to_string(caps);
TRACE("caps = %s\n", debugstr_a(human_readable));
g_free(human_readable);
- }
- if (FAILED(MFCreateMediaType(&media_type)))
- {
return NULL;
- }
- info = gst_caps_get_structure(caps, 0);
- mime_type = gst_structure_get_name(info);
- if (!(strncmp(mime_type, "video", 5)))
There's a lot of superfluous parentheses after local not operators in this series; this one seems especially superfluous.
👌
- {
GstVideoInfo video_info;
if (!(gst_video_info_from_caps(&video_info, caps)))
{
return NULL;
}
IMFMediaType_SetGUID(media_type, &MF_MT_MAJOR_TYPE, &MFMediaType_Video);
IMFMediaType_SetUINT64(media_type, &MF_MT_FRAME_SIZE, ((UINT64)video_info.width << 32) | video_info.height);
IMFMediaType_SetUINT64(media_type, &MF_MT_FRAME_RATE, ((UINT64)video_info.fps_n << 32) | video_info.fps_d);
if (!(strcmp(mime_type, "video/x-raw")))
{
GUID fourcc_subtype = MFVideoFormat_Base;
unsigned int i;
IMFMediaType_SetUINT32(media_type, &MF_MT_COMPRESSED, FALSE);
/* First try FOURCC */
if ((fourcc_subtype.Data1 = gst_video_format_to_fourcc(video_info.finfo->format)))
{
IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &fourcc_subtype);
}
else
{
for (i = 0; i < ARRAY_SIZE(uncompressed_formats); i++)
{
if (uncompressed_formats[i].format == video_info.finfo->format)
{
IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, uncompressed_formats[i].subtype);
break;
}
}
if (i == ARRAY_SIZE(uncompressed_formats))
FIXME("Unrecognized format.\n");
In which case the function should probably fail.
👌
}
}
else
{
FIXME("Unrecognized video format %s\n", mime_type);
IMFMediaType_Release(media_type);
return NULL;
}
- }
- else if (!(strncmp(mime_type, "audio", 5)))
- {
gint rate, channels, bitrate;
guint64 channel_mask;
IMFMediaType_SetGUID(media_type, &MF_MT_MAJOR_TYPE, &MFMediaType_Audio);
if (gst_structure_get_int(info, "rate", &rate))
IMFMediaType_SetUINT32(media_type, &MF_MT_AUDIO_SAMPLES_PER_SECOND, rate);
if (gst_structure_get_int(info, "channels", &channels))
IMFMediaType_SetUINT32(media_type, &MF_MT_AUDIO_NUM_CHANNELS, channels);
if (gst_structure_get(info, "channel-mask", GST_TYPE_BITMASK, &channel_mask, NULL))
IMFMediaType_SetUINT32(media_type, &MF_MT_AUDIO_CHANNEL_MASK, (DWORD)channel_mask);
Superfluous cast.
👌
if (gst_structure_get_int(info, "bitrate", &bitrate))
IMFMediaType_SetUINT32(media_type, &MF_MT_AVG_BITRATE, bitrate);
if (!(strcmp(mime_type, "audio/x-raw")))
{
const char *format;
if ((format = gst_structure_get_string(info, "format")))
You may find it easier to use gst_audio_info_from_caps() here.
Does this work on compressed audio types as well?
{
char type;
unsigned int bits_per_sample;
char endian[2];
char new_format[6];
if ((strlen(format) > 5) || (sscanf(format, "%c%u%2c", &type, &bits_per_sample, endian) < 2))
{
FIXME("Unhandled audio format %s\n", format);
IMFMediaType_Release(media_type);
return NULL;
}
if (type == 'F')
{
IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &MFAudioFormat_Float);
}
else if (type == 'U' || type == 'S')
{
IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &MFAudioFormat_PCM);
if (bits_per_sample == 8)
type = 'U';
else
type = 'S';
}
else
{
FIXME("Unrecognized audio format: %s\n", format);
IMFMediaType_Release(media_type);
return NULL;
}
IMFMediaType_SetUINT32(media_type, &MF_MT_AUDIO_BITS_PER_SAMPLE, bits_per_sample);
if (endian[0] == 'B')
endian[0] = 'L';
sprintf(new_format, "%c%u%.2s", type, bits_per_sample, bits_per_sample > 8 ? endian : 0);
gst_caps_set_simple(caps, "format", G_TYPE_STRING, new_format, NULL);
}
else
{
ERR("Failed to get audio format\n");
}
}
else
{
FIXME("Unrecognized audio format %s\n", mime_type);
IMFMediaType_Release(media_type);
return NULL;
}
- }
- else
- {
IMFMediaType_Release(media_type);
return NULL;
- }
- return media_type;
+}
+/* returns NULL if doesn't match exactly */ +IMFMediaType *mf_media_type_from_caps(GstCaps *caps) +{
- GstCaps *writeable_caps;
- IMFMediaType *ret;
- writeable_caps = gst_caps_copy(caps);
- ret = transform_to_media_type(writeable_caps);
- if (!(gst_caps_is_equal(caps, writeable_caps)))
- {
IMFMediaType_Release(ret);
ret = NULL;
- }
- gst_caps_unref(writeable_caps);
- return ret;
+}
+GstCaps *make_mf_compatible_caps(GstCaps *caps) +{
- GstCaps *ret;
- IMFMediaType *media_type;
- ret = gst_caps_copy(caps);
- if ((media_type = transform_to_media_type(ret)))
IMFMediaType_Release(media_type);
- if (!media_type)
- {
gst_caps_unref(ret);
return NULL;
- }
- return ret;
+}