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

Derek Lesho dlesho at codeweavers.com
Wed Aug 26 13:59:12 CDT 2020


Signed-off-by: Derek Lesho <dlesho at codeweavers.com>
---
 dlls/winegstreamer/gst_cbs.c      |  13 ++
 dlls/winegstreamer/gst_cbs.h      |   8 ++
 dlls/winegstreamer/gst_private.h  |   4 +
 dlls/winegstreamer/media_source.c | 137 +++++++++++++++++++-
 dlls/winegstreamer/mfplat.c       | 208 ++++++++++++++++++++++++++++++
 5 files changed, 367 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 71ca4290885..780cf1b02fa 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,9 @@ void start_dispatch_thread(void) DECLSPEC_HIDDEN;
 
 extern HRESULT mfplat_get_class_object(REFCLSID rclsid, REFIID riid, void **obj) DECLSPEC_HIDDEN;
 
+GstCaps *make_mf_compatible_caps(GstCaps *caps);
+IMFMediaType *mf_media_type_from_caps(GstCaps *caps);
+
 enum source_type
 {
     SOURCE_TYPE_MPEG_4,
diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c
index fa0e1065ea7..f08d46c48c5 100644
--- a/dlls/winegstreamer/media_source.c
+++ b/dlls/winegstreamer/media_source.c
@@ -38,8 +38,10 @@ 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;
     /* usually reflects state of source */
     enum
     {
@@ -47,6 +49,9 @@ struct media_stream
         STREAM_INACTIVE,
         STREAM_SHUTDOWN,
     } state;
+    /* used when in STUB state: */
+    DWORD stream_id;
+    HANDLE caps_event;
 };
 
 struct media_source
@@ -305,6 +310,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)
@@ -386,7 +393,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)
@@ -429,9 +439,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;
 
@@ -448,10 +461,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);
@@ -466,6 +479,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);
+
+    stream_type = mf_media_type_from_caps(stream->their_caps);
+    gst_caps_unref(stream->their_caps);
+    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);
+    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);
@@ -684,6 +734,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 */
+
+    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;
@@ -701,6 +768,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. */
+    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;
@@ -741,6 +838,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;
+    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, enum source_type type, struct media_source **out_media_source)
 {
     GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE(
@@ -793,6 +910,8 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, enum source_t
 
     gst_bin_add(GST_BIN(object->container), object->decodebin);
 
+    if(!GetEnvironmentVariableA("MF_DECODE_IN_SOURCE", NULL, 0))
+        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);
@@ -818,6 +937,12 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, enum source_t
     }
 
     WaitForSingleObject(object->all_streams_event, INFINITE);
+    for (unsigned int i = 0; i < object->stream_count; i++)
+    {
+        WaitForSingleObject(object->streams[i]->caps_event, INFINITE);
+        if (FAILED(hr = media_stream_init_desc(object->streams[i])))
+            goto fail;
+    }
 
     object->state = SOURCE_STOPPED;
 
@@ -1349,6 +1474,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 16e55247de1..06f7984bbe6 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"
@@ -442,3 +447,206 @@ HRESULT mfplat_get_class_object(REFCLSID rclsid, REFIID riid, void **obj)
 
     return CLASS_E_CLASSNOTAVAILABLE;
 }
+
+const static struct
+{
+    const GUID *subtype;
+    GstVideoFormat format;
+}
+uncompressed_formats[] =
+{
+    {&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 */
+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)))
+    {
+        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");
+            }
+        }
+        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);
+
+        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")))
+            {
+                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;
+}
\ No newline at end of file
-- 
2.28.0




More information about the wine-devel mailing list