[RFC PATCH 16/16] winegstreamer: Implement decoder MFT on gstreamer.

Derek Lesho dlesho at codeweavers.com
Wed Mar 25 19:12:41 CDT 2020


---
 dlls/winegstreamer/Makefile.in               |    1 +
 dlls/winegstreamer/gst_cbs.c                 |   65 +
 dlls/winegstreamer/gst_cbs.h                 |   18 +
 dlls/winegstreamer/gst_private.h             |    7 +
 dlls/winegstreamer/main.c                    |    3 +-
 dlls/winegstreamer/mf_decode.c               | 1210 ++++++++++++++++++
 dlls/winegstreamer/mfplat.c                  |  143 ++-
 dlls/winegstreamer/winegstreamer_classes.idl |   12 +
 include/mfidl.idl                            |    4 +-
 9 files changed, 1460 insertions(+), 3 deletions(-)
 create mode 100644 dlls/winegstreamer/mf_decode.c

diff --git a/dlls/winegstreamer/Makefile.in b/dlls/winegstreamer/Makefile.in
index 61789884b8..9ca63e926c 100644
--- a/dlls/winegstreamer/Makefile.in
+++ b/dlls/winegstreamer/Makefile.in
@@ -12,6 +12,7 @@ C_SRCS = \
 	main.c \
 	media_source.c \
 	mediatype.c \
+	mf_decode.c \
 	mfplat.c \
 	pin.c \
 	qualitycontrol.c \
diff --git a/dlls/winegstreamer/gst_cbs.c b/dlls/winegstreamer/gst_cbs.c
index 9c27c3490d..4ba58e87b0 100644
--- a/dlls/winegstreamer/gst_cbs.c
+++ b/dlls/winegstreamer/gst_cbs.c
@@ -51,6 +51,8 @@ static void CALLBACK perform_cb(TP_CALLBACK_INSTANCE *instance, void *user)
         perform_cb_gstdemux(cbdata);
     else if (cbdata->type < MEDIA_SOURCE_MAX)
         perform_cb_media_source(cbdata);
+    else if (cbdata->type < MF_DECODE_MAX)
+        perform_cb_mf_decode(cbdata);
 
     pthread_mutex_lock(&cbdata->lock);
     cbdata->finished = 1;
@@ -429,4 +431,67 @@ GstBusSyncReply watch_source_bus_wrapper(GstBus *bus, GstMessage *message, gpoin
     call_cb(&cbdata);
 
     return cbdata.u.watch_bus_data.ret;
+}
+
+gboolean activate_push_mode_wrapper(GstPad *pad, GstObject *parent, GstPadMode mode, gboolean activate)
+{
+    struct cb_data cbdata = { ACTIVATE_PUSH_MODE };
+
+    cbdata.u.activate_mode_data.pad = pad;
+    cbdata.u.activate_mode_data.parent = parent;
+    cbdata.u.activate_mode_data.mode = mode;
+    cbdata.u.activate_mode_data.activate = activate;
+
+    call_cb(&cbdata);
+
+    return cbdata.u.query_function_data.ret;
+}
+
+gboolean query_input_src_wrapper(GstPad *pad, GstObject *parent, GstQuery *query)
+{
+    struct cb_data cbdata = { QUERY_INPUT_SRC };
+
+    cbdata.u.query_function_data.pad = pad;
+    cbdata.u.query_function_data.parent = parent;
+    cbdata.u.query_function_data.query = query;
+
+    call_cb(&cbdata);
+
+    return cbdata.u.query_function_data.ret;
+}
+
+GstBusSyncReply watch_decoder_bus_wrapper(GstBus *bus, GstMessage *message, gpointer user)
+{
+    struct cb_data cbdata = { WATCH_DECODER_BUS };
+
+    cbdata.u.watch_bus_data.bus = bus;
+    cbdata.u.watch_bus_data.msg = message;
+    cbdata.u.watch_bus_data.user = user;
+
+    call_cb(&cbdata);
+
+    return cbdata.u.watch_bus_data.ret;
+}
+
+void decoder_pad_added_wrapper(GstElement *element, GstPad *pad, gpointer user)
+{
+    struct cb_data cbdata = { DECODER_PAD_ADDED };
+
+    cbdata.u.pad_added_data.element = element;
+    cbdata.u.pad_added_data.pad = pad;
+    cbdata.u.pad_added_data.user = user;
+
+    call_cb(&cbdata);
+}
+
+GstFlowReturn decoder_new_sample_wrapper(GstElement *appsink, gpointer user)
+{
+    struct cb_data cbdata = {DECODER_NEW_SAMPLE};
+
+    cbdata.u.new_sample_data.appsink = appsink;
+    cbdata.u.new_sample_data.user = user;
+
+    call_cb(&cbdata);
+
+    return cbdata.u.new_sample_data.ret;
 }
\ No newline at end of file
diff --git a/dlls/winegstreamer/gst_cbs.h b/dlls/winegstreamer/gst_cbs.h
index 2c83f294f4..e1e018fa46 100644
--- a/dlls/winegstreamer/gst_cbs.h
+++ b/dlls/winegstreamer/gst_cbs.h
@@ -55,6 +55,12 @@ enum CB_TYPE {
     STREAM_EOS,
     WATCH_SOURCE_BUS,
     MEDIA_SOURCE_MAX,
+    ACTIVATE_PUSH_MODE,
+    QUERY_INPUT_SRC,
+    DECODER_NEW_SAMPLE,
+    WATCH_DECODER_BUS,
+    DECODER_PAD_ADDED,
+    MF_DECODE_MAX,
 };
 
 struct cb_data {
@@ -148,6 +154,12 @@ struct cb_data {
             GstElement *appsink;
             gpointer user;
         } eos_data;
+        struct chain_data {
+            GstPad *pad;
+            GstObject *parent;
+            GstBuffer *buffer;
+            GstFlowReturn ret;
+        } chain_data;
     } u;
 
     int finished;
@@ -159,6 +171,7 @@ struct cb_data {
 void mark_wine_thread(void) DECLSPEC_HIDDEN;
 void perform_cb_gstdemux(struct cb_data *data) DECLSPEC_HIDDEN;
 void perform_cb_media_source(struct cb_data *data) DECLSPEC_HIDDEN;
+void perform_cb_mf_decode(struct cb_data *data) DECLSPEC_HIDDEN;
 
 GstBusSyncReply watch_bus_wrapper(GstBus *bus, GstMessage *msg, gpointer user) DECLSPEC_HIDDEN;
 void existing_new_pad_wrapper(GstElement *bin, GstPad *pad, gpointer user) DECLSPEC_HIDDEN;
@@ -185,5 +198,10 @@ void source_all_streams_wrapper(GstElement *element, gpointer user) DECLSPEC_HID
 GstFlowReturn stream_new_sample_wrapper(GstElement *appsink, gpointer user) DECLSPEC_HIDDEN;
 void stream_eos_wrapper(GstElement *appsink, gpointer user) DECLSPEC_HIDDEN;
 GstBusSyncReply watch_source_bus_wrapper(GstBus *bus, GstMessage *message, gpointer user) DECLSPEC_HIDDEN;
+gboolean activate_push_mode_wrapper(GstPad *pad, GstObject *parent, GstPadMode mode, gboolean activate) DECLSPEC_HIDDEN;
+gboolean query_input_src_wrapper(GstPad *pad, GstObject *parent, GstQuery *query) DECLSPEC_HIDDEN;
+GstBusSyncReply watch_decoder_bus_wrapper(GstBus *bus, GstMessage *message, gpointer user) DECLSPEC_HIDDEN;
+GstFlowReturn decoder_new_sample_wrapper(GstElement *appsink, gpointer user) DECLSPEC_HIDDEN;
+void decoder_pad_added_wrapper(GstElement *element, GstPad *Pad, gpointer user) DECLSPEC_HIDDEN;
 
 #endif
diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h
index 489b543738..4e9cc53a18 100644
--- a/dlls/winegstreamer/gst_private.h
+++ b/dlls/winegstreamer/gst_private.h
@@ -53,6 +53,7 @@ BOOL init_gstreamer(void) DECLSPEC_HIDDEN;
 
 void start_dispatch_thread(void) DECLSPEC_HIDDEN;
 
+extern HRESULT mfplat_DllRegisterServer(void) DECLSPEC_HIDDEN;
 extern HRESULT mfplat_get_class_object(REFCLSID rclsid, REFIID riid, void **obj) DECLSPEC_HIDDEN;
 
 IMFMediaType* media_type_from_caps(GstCaps *caps);
@@ -60,6 +61,12 @@ GstCaps *caps_from_media_type(IMFMediaType *type);
 IMFSample* mf_sample_from_gst_buffer(GstBuffer *in);
 GstBuffer* gst_buffer_from_mf_sample(IMFSample *in);
 
+enum decoder_type
+{
+    DECODER_TYPE_H264,
+    DECODER_TYPE_AAC,
+};
+HRESULT generic_decoder_construct(REFIID riid, void **obj, enum decoder_type);
 enum source_type
 {
     SOURCE_TYPE_MPEG_4,
diff --git a/dlls/winegstreamer/main.c b/dlls/winegstreamer/main.c
index 2872710b3e..12ca11fa6c 100644
--- a/dlls/winegstreamer/main.c
+++ b/dlls/winegstreamer/main.c
@@ -365,7 +365,8 @@ HRESULT WINAPI DllRegisterServer(void)
     IFilterMapper2_RegisterFilter(mapper, &CLSID_WAVEParser, wave_parserW, NULL, NULL, NULL, &reg_wave_parser);
 
     IFilterMapper2_Release(mapper);
-    return S_OK;
+
+    return mfplat_DllRegisterServer();
 }
 
 HRESULT WINAPI DllUnregisterServer(void)
diff --git a/dlls/winegstreamer/mf_decode.c b/dlls/winegstreamer/mf_decode.c
new file mode 100644
index 0000000000..9c36f94fdc
--- /dev/null
+++ b/dlls/winegstreamer/mf_decode.c
@@ -0,0 +1,1210 @@
+#include "config.h"
+
+#include <gst/gst.h>
+
+#include "gst_private.h"
+#include "gst_cbs.h"
+
+#include <stdarg.h>
+
+#define COBJMACROS
+#define NONAMELESSUNION
+
+#include "mfapi.h"
+#include "mferror.h"
+#include "mfobjects.h"
+#include "mftransform.h"
+
+#include "wine/debug.h"
+#include "wine/heap.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(mfplat);
+
+const GUID *h264_input_types[] = {&MFVideoFormat_H264};
+const GUID *h264_output_types[] = {&MFVideoFormat_I420, &MFVideoFormat_IYUV, &MFVideoFormat_NV12, &MFVideoFormat_YUY2, &MFVideoFormat_YV12};
+
+const GUID *aac_input_types[] = {&MFAudioFormat_AAC};
+const GUID *aac_output_types[] = {&MFAudioFormat_Float};
+
+static struct decoder_desc
+{
+    const GUID *major_type;
+    const GUID **input_types;
+    unsigned int input_types_count;
+    const GUID **output_types;
+    unsigned int output_types_count;
+} decoder_descs[] =
+{
+    { /* DECODER_TYPE_H264 */
+        &MFMediaType_Video,
+        h264_input_types,
+        ARRAY_SIZE(h264_input_types),
+        h264_output_types,
+        ARRAY_SIZE(h264_output_types),
+    },
+    { /* DECODER_TYPE_AAC */
+        &MFMediaType_Audio,
+        aac_input_types,
+        ARRAY_SIZE(aac_input_types),
+        aac_output_types,
+        ARRAY_SIZE(aac_output_types),
+    }
+};
+
+struct mf_decoder
+{
+    IMFTransform IMFTransform_iface;
+    IMFAsyncCallback process_message_callback;
+    LONG refcount;
+    enum decoder_type type;
+    BOOL video;
+    IMFMediaType *input_type, *output_type;
+    BOOL valid_state;
+    GstBus *bus;
+    GstElement *container;
+    GstElement *parser, *decoder, *converter, *appsink;
+    GstPad *input_src, *their_sink;
+    unsigned int output_counter;
+    BOOL draining, eos;
+    CRITICAL_SECTION state_cs;
+    CONDITION_VARIABLE state_cv;
+};
+
+static struct mf_decoder *impl_mf_decoder_from_IMFTransform(IMFTransform *iface)
+{
+    return CONTAINING_RECORD(iface, struct mf_decoder, IMFTransform_iface);
+}
+
+static struct mf_decoder *impl_from_message_callback_IMFAsyncCallback(IMFAsyncCallback *iface)
+{
+    return CONTAINING_RECORD(iface, struct mf_decoder, process_message_callback);
+}
+
+static HRESULT WINAPI mf_decoder_QueryInterface (IMFTransform *iface, REFIID riid, void **out)
+{
+    TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), out);
+
+    if (IsEqualIID(riid, &IID_IMFTransform) ||
+            IsEqualIID(riid, &IID_IUnknown))
+    {
+        *out = iface;
+        IMFTransform_AddRef(iface);
+        return S_OK;
+    }
+
+    WARN("Unsupported %s.\n", debugstr_guid(riid));
+    *out = NULL;
+    return E_NOINTERFACE;
+}
+
+static ULONG WINAPI mf_decoder_AddRef(IMFTransform *iface)
+{
+    struct mf_decoder *This = impl_mf_decoder_from_IMFTransform(iface);
+    ULONG refcount = InterlockedIncrement(&This->refcount);
+
+    TRACE("%p, refcount %u.\n", iface, refcount);
+
+    return refcount;
+}
+
+static void mf_decoder_destroy(struct mf_decoder *decoder);
+static ULONG WINAPI mf_decoder_Release(IMFTransform *iface)
+{
+    struct mf_decoder *This = impl_mf_decoder_from_IMFTransform(iface);
+    ULONG refcount = InterlockedDecrement(&This->refcount);
+
+    TRACE("%p, refcount %u.\n", iface, refcount);
+
+    if (!refcount)
+    {
+        mf_decoder_destroy(This);
+    }
+
+    return refcount;
+}
+
+static HRESULT WINAPI mf_decoder_GetStreamLimits(IMFTransform *iface, DWORD *input_minimum, DWORD *input_maximum,
+        DWORD *output_minimum, DWORD *output_maximum)
+{
+    TRACE("%p, %p, %p, %p, %p.\n", iface, input_minimum, input_maximum, output_minimum, output_maximum);
+
+    *input_minimum = *input_maximum = *output_minimum = *output_maximum = 1;
+
+    return S_OK;
+}
+
+static HRESULT WINAPI mf_decoder_GetStreamCount(IMFTransform *iface, DWORD *inputs, DWORD *outputs)
+{
+    TRACE("%p %p %p.\n", iface, inputs, outputs);
+
+    *inputs = *outputs = 1;
+
+    return S_OK;
+}
+
+static HRESULT WINAPI mf_decoder_GetStreamIDs(IMFTransform *iface, DWORD input_size, DWORD *inputs,
+        DWORD output_size, DWORD *outputs)
+{
+    TRACE("%p %u %p %u %p.\n", iface, input_size, inputs, output_size, outputs);
+
+    return E_NOTIMPL;
+}
+
+
+static HRESULT WINAPI mf_decoder_GetInputStreamInfo(IMFTransform *iface, DWORD id, MFT_INPUT_STREAM_INFO *info)
+{
+    struct mf_decoder *This = impl_mf_decoder_from_IMFTransform(iface);
+
+    TRACE("%p %u %p\n", This, id, info);
+
+    if (id != 0)
+        return MF_E_INVALIDSTREAMNUMBER;
+
+    /* If we create a wrapped GstBuffer, remove MFT_INPUT_STREAM_DOES_NOT_ADDREF */
+    info->dwFlags = MFT_INPUT_STREAM_WHOLE_SAMPLES | MFT_INPUT_STREAM_DOES_NOT_ADDREF;
+    info->cbMaxLookahead = 0;
+    info->cbAlignment = 0;
+    /* this is incorrect */
+    info->hnsMaxLatency = 0;
+    return S_OK;
+}
+
+static HRESULT WINAPI mf_decoder_GetOutputStreamInfo(IMFTransform *iface, DWORD id, MFT_OUTPUT_STREAM_INFO *info)
+{
+    struct mf_decoder *This = impl_mf_decoder_from_IMFTransform(iface);
+    MFT_OUTPUT_STREAM_INFO stream_info = {};
+
+    TRACE("%p %u %p\n", This, id, info);
+
+    if (id != 0)
+        return MF_E_INVALIDSTREAMNUMBER;
+
+    stream_info.dwFlags = MFT_OUTPUT_STREAM_PROVIDES_SAMPLES;
+    stream_info.cbSize = 0;
+    stream_info.cbAlignment = 0;
+
+    *info = stream_info;
+
+    return S_OK;
+}
+
+static HRESULT WINAPI mf_decoder_GetAttributes(IMFTransform *iface, IMFAttributes **attributes)
+{
+    FIXME("%p, %p. stub!\n", iface, attributes);
+
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI mf_decoder_GetInputStreamAttributes(IMFTransform *iface, DWORD id,
+        IMFAttributes **attributes)
+{
+    FIXME("%p, %u, %p. stub!\n", iface, id, attributes);
+
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI mf_decoder_GetOutputStreamAttributes(IMFTransform *iface, DWORD id,
+        IMFAttributes **attributes)
+{
+    FIXME("%p, %u, %p. stub!\n", iface, id, attributes);
+
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI mf_decoder_DeleteInputStream(IMFTransform *iface, DWORD id)
+{
+    FIXME("%p, %u. stub!\n", iface, id);
+
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI mf_decoder_AddInputStreams(IMFTransform *iface, DWORD streams, DWORD *ids)
+{
+    FIXME("%p, %u, %p. stub!\n", iface, streams, ids);
+
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI mf_decoder_GetInputAvailableType(IMFTransform *iface, DWORD id, DWORD index,
+        IMFMediaType **type)
+{
+    struct mf_decoder *This = impl_mf_decoder_from_IMFTransform(iface);
+    IMFMediaType *input_type;
+    HRESULT hr;
+
+    TRACE("%p, %u, %u, %p\n", This, id, index, type);
+
+    if (id != 0)
+        return MF_E_INVALIDSTREAMNUMBER;
+
+    if (index >= decoder_descs[This->type].input_types_count)
+        return MF_E_NO_MORE_TYPES;
+
+    if (FAILED(hr = MFCreateMediaType(&input_type)))
+        return hr;
+
+    if (FAILED(hr = IMFMediaType_SetGUID(input_type, &MF_MT_MAJOR_TYPE, decoder_descs[This->type].major_type)))
+    {
+        IMFMediaType_Release(input_type);
+        return hr;
+    }
+
+    if (FAILED(hr = IMFMediaType_SetGUID(input_type, &MF_MT_SUBTYPE, decoder_descs[This->type].input_types[index])))
+    {
+        IMFMediaType_Release(input_type);
+        return hr;
+    }
+
+    *type = input_type;
+
+    return S_OK;
+}
+
+static HRESULT WINAPI mf_decoder_GetOutputAvailableType(IMFTransform *iface, DWORD id, DWORD index,
+        IMFMediaType **type)
+{
+    struct mf_decoder *This = impl_mf_decoder_from_IMFTransform(iface);
+    IMFMediaType *output_type;
+    HRESULT hr;
+
+    TRACE("%p, %u, %u, %p\n", This, id, index, type);
+
+    if (id != 0)
+        return MF_E_INVALIDSTREAMNUMBER;
+
+    if (!(This->input_type))
+        return MF_E_TRANSFORM_TYPE_NOT_SET;
+
+    if (index >= decoder_descs[This->type].output_types_count)
+        return MF_E_NO_MORE_TYPES;
+
+    if (FAILED(hr = MFCreateMediaType(&output_type)))
+        return hr;
+
+    /* TODO: This may need to be more fine tuned */
+    if (FAILED(hr = IMFMediaType_CopyAllItems(This->input_type, (IMFAttributes*) output_type)))
+    {
+        IMFMediaType_Release(output_type);
+        return hr;
+    }
+
+    if (FAILED(hr = IMFMediaType_SetGUID(output_type, &MF_MT_MAJOR_TYPE, decoder_descs[This->type].major_type)))
+    {
+        IMFMediaType_Release(output_type);
+        return hr;
+    }
+
+    if (FAILED(hr = IMFMediaType_SetGUID(output_type, &MF_MT_SUBTYPE, decoder_descs[This->type].output_types[index])))
+    {
+        IMFMediaType_Release(output_type);
+        return hr;
+    }
+
+    *type = output_type;
+
+    return S_OK;
+}
+
+static gboolean activate_push_mode(GstPad *pad, GstObject *parent, GstPadMode mode, gboolean activate)
+{
+    TRACE("%s mft input pad in %s mode.\n",
+            activate ? "Activating" : "Deactivating", gst_pad_mode_get_name(mode));
+
+    switch (mode) {
+        case GST_PAD_MODE_PUSH:
+            return TRUE;
+        default:
+            return FALSE;
+    }
+}
+
+static gboolean query_input_src(GstPad *pad, GstObject *parent, GstQuery *query)
+{
+    struct mf_decoder *This = gst_pad_get_element_private(pad);
+
+    TRACE("GStreamer queries MFT Input Pad %p for %s\n", This, GST_QUERY_TYPE_NAME(query));
+
+    switch (GST_QUERY_TYPE(query))
+    {
+        case GST_QUERY_CAPS:
+        {
+            gst_query_set_caps_result(query, caps_from_media_type(This->input_type));
+            return TRUE;
+        }
+        case GST_QUERY_SCHEDULING:
+        {
+            gst_query_add_scheduling_mode(query, GST_PAD_MODE_PUSH);
+            return TRUE;
+        }
+        case GST_QUERY_SEEKING:
+        {
+            GstFormat format;
+            gboolean seekable;
+            gint64 segment_start, segment_end;
+
+            gst_query_parse_seeking(query, &format, &seekable, &segment_start, &segment_end);
+            gst_query_set_seeking(query, format, 0, segment_start, segment_end);
+            return TRUE;
+        }
+        case GST_QUERY_DURATION:
+        {
+            return FALSE;
+        }
+        case GST_QUERY_LATENCY:
+        {
+            return FALSE;
+        }
+        default:
+        {
+            ERR("Unhandled query type %s on MFT Input Pad %p\n", GST_QUERY_TYPE_NAME(query), This);
+            return gst_pad_query_default (pad, parent, query);
+        }
+    }
+}
+
+static GstFlowReturn decoder_new_sample(GstElement *appsink, gpointer user)
+{
+    struct mf_decoder *This = (struct mf_decoder *) user;
+
+    This->output_counter++;
+
+    return GST_FLOW_OK;
+}
+
+static BOOL find_decoder_from_caps(GstCaps *input_caps, GstElement **decoder, GstElement **parser)
+{
+    GList *parser_list_one, *parser_list_two;
+    GList *walk;
+    BOOL ret = TRUE;
+
+    TRACE("input caps: %s\n", gst_caps_to_string(input_caps));
+
+    parser_list_one = gst_element_factory_list_get_elements(GST_ELEMENT_FACTORY_TYPE_PARSER, 1);
+    parser_list_two = gst_element_factory_list_filter(parser_list_one, input_caps, GST_PAD_SINK, 0);
+    gst_plugin_feature_list_free(parser_list_one);
+    parser_list_one = parser_list_two;
+    if (!(g_list_length(parser_list_one)))
+    {
+        GList *decoder_list_one, *decoder_list_two;
+        decoder_list_one = gst_element_factory_list_get_elements(GST_ELEMENT_FACTORY_TYPE_DECODER, 1);
+        decoder_list_two = gst_element_factory_list_filter(decoder_list_one, input_caps, GST_PAD_SINK, 0);
+        gst_plugin_feature_list_free(decoder_list_one);
+        decoder_list_one = decoder_list_two;
+        if (!(g_list_length(decoder_list_one)) ||
+            !(*decoder = gst_element_factory_create(g_list_first(decoder_list_one)->data, NULL)))
+        {
+            gst_plugin_feature_list_free(decoder_list_one);
+            ERR("Failed to create decoder\n");
+            ret = FALSE;
+            goto done;
+        }
+        TRACE("Found decoder %s\n", GST_ELEMENT_NAME(g_list_first(decoder_list_one)->data));
+    }
+    else
+    {
+        for (walk = (GList *) parser_list_one; walk; walk = g_list_next(walk))
+        {
+            GstElementFactory *parser_factory = walk->data;
+            const GList *templates, *walk_templ;
+
+            templates = gst_element_factory_get_static_pad_templates(parser_factory);
+
+            for (walk_templ = (GList *)templates; walk_templ; walk_templ = g_list_next(walk_templ))
+            {
+                GList *decoder_list_one, *decoder_list_two;
+                GstStaticPadTemplate *templ = walk_templ->data;
+                GstCaps *templ_caps;
+
+                if (templ->direction != GST_PAD_SRC)
+                    continue;
+
+                templ_caps = gst_static_pad_template_get_caps(templ);
+
+                TRACE("Matching parser src caps %s to decoder.\n", gst_caps_to_string(templ_caps));
+
+                decoder_list_one = gst_element_factory_list_get_elements(GST_ELEMENT_FACTORY_TYPE_DECODER, 1);
+                decoder_list_two = gst_element_factory_list_filter(decoder_list_one, templ_caps, GST_PAD_SINK, 0);
+                gst_plugin_feature_list_free(decoder_list_one);
+                decoder_list_one = decoder_list_two;
+                gst_caps_unref(templ_caps);
+
+                if (!(g_list_length(decoder_list_one)))
+                    continue;
+
+                if (!(*parser = gst_element_factory_create(parser_factory, NULL)))
+                {
+                    gst_plugin_feature_list_free(decoder_list_one);
+                    ERR("Failed to create parser\n");
+                    ret = FALSE;
+                    goto done;
+                }
+
+                if (!(*decoder = gst_element_factory_create(g_list_first(decoder_list_one)->data, NULL)))
+                {
+                    gst_plugin_feature_list_free(decoder_list_one);
+                    ERR("Failed to create decoder\n");
+                    ret = FALSE;
+                    goto done;
+                }
+
+                TRACE("Found decoder %s parser %s\n",
+                GST_ELEMENT_NAME(g_list_first(decoder_list_one)->data), GST_ELEMENT_NAME(parser_factory));
+                gst_plugin_feature_list_free(decoder_list_one);
+
+                goto done;
+            }
+        }
+    }
+    done:
+    gst_plugin_feature_list_free(parser_list_one);
+    return ret;
+}
+
+static void decoder_update_pipeline(struct mf_decoder *This)
+{
+    GstSegment *segment;
+    GstCaps *input_caps = NULL;
+
+    This->valid_state = FALSE;
+
+    /* tear down current pipeline */
+    gst_element_set_state(This->container, GST_STATE_READY);
+    if (gst_element_get_state(This->container, NULL, NULL, -1) == GST_STATE_CHANGE_FAILURE)
+    {
+        ERR("Failed to stop container\n");
+    }
+
+    g_object_set(This->appsink, "caps", gst_caps_new_empty(), NULL);
+
+    if (This->input_src)
+    {
+        gst_pad_unlink(This->input_src, This->their_sink);
+        gst_object_unref(G_OBJECT(This->input_src));
+        This->input_src = NULL;
+    }
+
+    if (This->their_sink)
+    {
+        gst_object_unref(G_OBJECT(This->their_sink));
+        This->their_sink = NULL;
+    }
+
+    if (This->parser)
+    {
+        gst_element_unlink(This->parser, This->decoder);
+        gst_bin_remove(GST_BIN(This->container), This->parser);
+        This->parser = NULL;
+    }
+    if (This->decoder)
+    {
+        gst_element_unlink(This->decoder, This->converter);
+        gst_bin_remove(GST_BIN(This->container), This->decoder);
+        This->decoder = NULL;
+    }
+
+    /* we can only have a valid state if an input and output type is present */
+    if (!This->input_type || !This->output_type)
+        return;
+
+    /* We do leave a lot of unfreed objects here when we failure,
+       but it will be cleaned up on the next call */
+
+    input_caps = caps_from_media_type(This->input_type);
+
+    if (!(This->input_src = gst_pad_new_from_template(gst_pad_template_new(
+        "mf_src",
+        GST_PAD_SRC,
+        GST_PAD_ALWAYS,
+        input_caps
+        ), "input_src")))
+    {
+        ERR("Failed to create input source\n");
+        goto done;
+    }
+
+    gst_pad_set_activatemode_function(This->input_src, activate_push_mode_wrapper);
+    gst_pad_set_query_function(This->input_src, query_input_src_wrapper);
+    gst_pad_set_element_private(This->input_src, This);
+
+    if (!(find_decoder_from_caps(input_caps, &This->decoder, &This->parser)))
+    {
+        goto done;
+    }
+
+    gst_bin_add(GST_BIN(This->container), This->decoder);
+    if (This->parser)
+    {
+        gst_bin_add(GST_BIN(This->container), This->parser);
+    }
+
+    if (!(This->their_sink = gst_element_get_static_pad(This->parser ? This->parser : This->decoder, "sink")))
+    {
+        goto done;
+    }
+
+    g_object_set(This->appsink, "caps", caps_from_media_type(This->output_type), NULL);
+
+    if (gst_pad_link(This->input_src, This->their_sink) != GST_PAD_LINK_OK)
+    {
+        ERR("Failed to link input source to decoder sink\n");
+        return;
+    }
+
+    if (This->parser && !(gst_element_link(This->parser, This->decoder)))
+    {
+        ERR("Failed to link parser to decoder\n");
+        goto done;
+    }
+
+    if (!(gst_element_link(This->decoder, This->converter)))
+    {
+        ERR("Failed to link decoder to converter\n");
+        goto done;
+    }
+
+    gst_element_set_state(This->container, GST_STATE_PLAYING);
+
+    gst_pad_set_active(This->input_src, 1);
+    gst_pad_push_event(This->input_src, gst_event_new_stream_start("decoder-stream"));
+    gst_pad_push_event(This->input_src, gst_event_new_caps(caps_from_media_type(This->input_type)));
+    segment = gst_segment_new();
+    gst_segment_init(segment, GST_FORMAT_DEFAULT);
+    gst_pad_push_event(This->input_src, gst_event_new_segment(segment));
+
+    This->valid_state = TRUE;
+    done:
+    if (input_caps)
+        gst_caps_unref(input_caps);
+    return;
+}
+
+static HRESULT WINAPI mf_decoder_SetInputType(IMFTransform *iface, DWORD id, IMFMediaType *type, DWORD flags)
+{
+    struct mf_decoder *This = impl_mf_decoder_from_IMFTransform(iface);
+    HRESULT hr = S_OK;
+
+    TRACE("%p, %u, %p, %#x\n", This, id, type, flags);
+
+    if (id != 0)
+        return MF_E_INVALIDSTREAMNUMBER;
+
+    if (type)
+    {
+        GUID major_type, subtype;
+        BOOL found = FALSE;
+
+        if (FAILED(hr = IMFMediaType_GetGUID(type, &MF_MT_MAJOR_TYPE, &major_type)))
+            return hr;
+        if (FAILED(hr = IMFMediaType_GetGUID(type, &MF_MT_SUBTYPE, &subtype)))
+            return hr;
+
+        for (unsigned int i = 0; i < decoder_descs[This->type].input_types_count; i++)
+        {
+            UINT64 unused;
+
+            if (IsEqualGUID(&major_type, decoder_descs[This->type].major_type) &&
+                IsEqualGUID(&subtype, decoder_descs[This->type].input_types[i]))
+            {
+                if (This->video)
+                {
+                    if (FAILED(hr = IMFMediaType_GetUINT64(type, &MF_MT_FRAME_SIZE, &unused)))
+                        return hr;
+
+                    if (FAILED(hr = IMFMediaType_GetUINT64(type, &MF_MT_FRAME_RATE, &unused)))
+                        return hr;
+                }
+
+                found = TRUE;
+                break;
+            }
+        }
+
+        if (!found)
+            return MF_E_INVALIDTYPE;
+    }
+
+    if (flags & MFT_SET_TYPE_TEST_ONLY)
+    {
+        return S_OK;
+    }
+
+    EnterCriticalSection(&This->state_cs);
+
+    if (type)
+    {
+        if (!This->input_type)
+            if (FAILED(hr = MFCreateMediaType(&This->input_type)))
+                goto done;
+
+        if (FAILED(hr = IMFMediaType_CopyAllItems(type, (IMFAttributes*) This->input_type)))
+            goto done;
+    }
+    else if (This->input_type)
+    {
+        IMFMediaType_Release(This->input_type);
+        This->input_type = NULL;
+    }
+
+    decoder_update_pipeline(This);
+
+    done:
+    LeaveCriticalSection(&This->state_cs);
+    WakeAllConditionVariable(&This->state_cv);
+    return hr;
+}
+
+static HRESULT WINAPI mf_decoder_SetOutputType(IMFTransform *iface, DWORD id, IMFMediaType *type, DWORD flags)
+{
+    struct mf_decoder *This = impl_mf_decoder_from_IMFTransform(iface);
+    HRESULT hr = S_OK;
+
+    TRACE("%p, %u, %p, %#x\n", This, id, type, flags);
+
+    if (id != 0)
+        return MF_E_INVALIDSTREAMNUMBER;
+
+    if (type)
+    {
+        /* validate the type */
+
+        for (unsigned int i = 0; i < decoder_descs[This->type].output_types_count; i++)
+        {
+            GUID major_type, subtype;
+            UINT64 unused;
+
+            if (FAILED(hr = IMFMediaType_GetGUID(type, &MF_MT_MAJOR_TYPE, &major_type)))
+                return hr;
+            if (FAILED(hr = IMFMediaType_GetGUID(type, &MF_MT_SUBTYPE, &subtype)))
+                return hr;
+
+            if (IsEqualGUID(&major_type, decoder_descs[This->type].major_type) &&
+                IsEqualGUID(&subtype, decoder_descs[This->type].output_types[i]))
+            {
+                if (This->video)
+                {
+                    if (FAILED(hr = IMFMediaType_GetUINT64(type, &MF_MT_FRAME_SIZE, &unused)))
+                        return hr;
+
+                    if (FAILED(hr = IMFMediaType_GetUINT64(type, &MF_MT_FRAME_RATE, &unused)))
+                        return hr;
+                }
+
+                break;
+            }
+        }
+    }
+
+    if (flags & MFT_SET_TYPE_TEST_ONLY)
+    {
+        return S_OK;
+    }
+
+    EnterCriticalSection(&This->state_cs);
+    if (type)
+    {
+        if (!This->output_type)
+            if (FAILED(hr = MFCreateMediaType(&This->output_type)))
+                goto done;
+
+        if (FAILED(hr = IMFMediaType_CopyAllItems(type, (IMFAttributes*) This->output_type)))
+            goto done;
+    }
+    else if (This->output_type)
+    {
+        IMFMediaType_Release(This->output_type);
+        This->output_type = NULL;
+    }
+
+    decoder_update_pipeline(This);
+
+    done:
+    LeaveCriticalSection(&This->state_cs);
+    WakeAllConditionVariable(&This->state_cv);
+    return hr;
+}
+
+static HRESULT WINAPI mf_decoder_GetInputCurrentType(IMFTransform *iface, DWORD id, IMFMediaType **type)
+{
+    FIXME("%p, %u, %p. stub!\n", iface, id, type);
+
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI mf_decoder_GetOutputCurrentType(IMFTransform *iface, DWORD id, IMFMediaType **type)
+{
+    FIXME("%p, %u, %p. stub!\n", iface, id, type);
+
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI mf_decoder_GetInputStatus(IMFTransform *iface, DWORD id, DWORD *flags)
+{
+    struct mf_decoder *This = impl_mf_decoder_from_IMFTransform(iface);
+
+    TRACE("%p, %u, %p\n", This, id, flags);
+
+    *flags = This->output_counter ? MFT_INPUT_STATUS_ACCEPT_DATA : 0;
+
+    return S_OK;
+}
+
+static HRESULT WINAPI mf_decoder_GetOutputStatus(IMFTransform *iface, DWORD *flags)
+{
+    struct mf_decoder *This = impl_mf_decoder_from_IMFTransform(iface);
+
+    TRACE("%p, %p.\n", This, flags);
+
+    *flags = This->output_counter ? MFT_OUTPUT_STATUS_SAMPLE_READY : 0;
+
+    return S_OK;
+}
+
+static HRESULT WINAPI mf_decoder_SetOutputBounds(IMFTransform *iface, LONGLONG lower, LONGLONG upper)
+{
+    FIXME("%p, %s, %s. stub!\n", iface, wine_dbgstr_longlong(lower), wine_dbgstr_longlong(upper));
+
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI mf_decoder_ProcessEvent(IMFTransform *iface, DWORD id, IMFMediaEvent *event)
+{
+    FIXME("%p, %u, %p. stub!\n", iface, id, event);
+
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI decoder_process_message_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 ULONG WINAPI decoder_process_message_callback_AddRef(IMFAsyncCallback *iface)
+{
+    struct mf_decoder *decoder = impl_from_message_callback_IMFAsyncCallback(iface);
+    return IMFTransform_AddRef(&decoder->IMFTransform_iface);
+}
+
+static ULONG WINAPI decoder_process_message_callback_Release(IMFAsyncCallback *iface)
+{
+    struct mf_decoder *decoder = impl_from_message_callback_IMFAsyncCallback(iface);
+    return IMFTransform_Release(&decoder->IMFTransform_iface);
+}
+
+static HRESULT WINAPI decoder_process_message_callback_GetParameters(IMFAsyncCallback *iface,
+        DWORD *flags, DWORD *queue)
+{
+    return E_NOTIMPL;
+}
+
+const GUID WINE_MFT_MESSAGE_TYPE = {0xd09998bf, 0x102f, 0x4efa, {0x8f,0x84,0x06,0x1f,0xa4,0x10,0xf2,0x64}};
+
+static HRESULT WINAPI decoder_process_message_callback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result)
+{
+    struct mf_decoder *This = impl_from_message_callback_IMFAsyncCallback(iface);
+    IUnknown *state;
+    IMFAttributes *async_param;
+    MFT_MESSAGE_TYPE message_type;
+    HRESULT hr;
+
+    state = IMFAsyncResult_GetStateNoAddRef(result);
+    if (!state)
+        return E_FAIL;
+    if (FAILED(hr = IUnknown_QueryInterface(state, &IID_IMFAttributes, (void **)&async_param)))
+        return hr;
+    if (FAILED(hr = IMFAttributes_GetUINT32(async_param, &WINE_MFT_MESSAGE_TYPE, &message_type)))
+    {
+        IMFAttributes_Release(async_param);
+        return hr;
+    }
+    IMFAttributes_Release(async_param);
+
+    switch (message_type)
+    {
+        case MFT_MESSAGE_COMMAND_DRAIN:
+        {
+            EnterCriticalSection(&This->state_cs);
+            This->draining = TRUE;
+            LeaveCriticalSection(&This->state_cs);
+            WakeAllConditionVariable(&This->state_cv);
+            gst_pad_push_event(This->input_src, gst_event_new_eos());
+
+            EnterCriticalSection(&This->state_cs);
+            while(This->eos == FALSE)
+            {
+                SleepConditionVariableCS(&This->state_cv, &This->state_cs, INFINITE);
+            }
+            gst_pad_push_event(This->input_src, gst_event_new_flush_stop(0));
+            This->draining = FALSE;
+            LeaveCriticalSection(&This->state_cs);
+            WakeAllConditionVariable(&This->state_cv);
+            return S_OK;
+        }
+        default:
+            return E_FAIL;
+    }
+}
+
+static const IMFAsyncCallbackVtbl process_message_callback_vtbl =
+{
+    decoder_process_message_callback_QueryInterface,
+    decoder_process_message_callback_AddRef,
+    decoder_process_message_callback_Release,
+    decoder_process_message_callback_GetParameters,
+    decoder_process_message_callback_Invoke,
+};
+
+static HRESULT WINAPI mf_decoder_ProcessMessage(IMFTransform *iface, MFT_MESSAGE_TYPE message, ULONG_PTR param)
+{
+    struct mf_decoder *This = impl_mf_decoder_from_IMFTransform(iface);
+    IMFAttributes *async_param;
+    HRESULT hr;
+
+    TRACE("%p, %u %lu.\n", This, message, param);
+
+    if (FAILED(hr = MFCreateAttributes(&async_param, 1)))
+        return hr;
+    EnterCriticalSection(&This->state_cs);
+
+    switch (message)
+    {
+        case MFT_MESSAGE_COMMAND_DRAIN:
+        {
+            if (This->draining)
+            {
+                hr = S_OK;
+                break;
+            }
+
+            IMFAttributes_SetUINT32(async_param, &WINE_MFT_MESSAGE_TYPE, message);
+
+            MFPutWorkItem(MF_MULTITHREADED_WORKQUEUE, &This->process_message_callback, (IUnknown *)async_param);
+            while (!This->draining)
+                SleepConditionVariableCS(&This->state_cv, &This->state_cs, INFINITE);
+
+            hr = S_OK;
+            break;
+        }
+        case MFT_MESSAGE_NOTIFY_BEGIN_STREAMING:
+        {
+            hr = S_OK;
+            break;
+        }
+        default:
+        {
+            ERR("Unhandled message type %u.\n", message);
+            hr = E_FAIL;
+            break;
+        }
+    }
+
+    LeaveCriticalSection(&This->state_cs);
+    IMFAttributes_Release(async_param);
+    return hr;
+}
+
+static HRESULT WINAPI mf_decoder_ProcessInput(IMFTransform *iface, DWORD id, IMFSample *sample, DWORD flags)
+{
+    struct mf_decoder *This = impl_mf_decoder_from_IMFTransform(iface);
+    GstBuffer *gst_buffer;
+    GstFlowReturn ret;
+    HRESULT hr = S_OK;
+
+    TRACE("%p, %u, %p, %#x\n", This, id, sample, flags);
+
+    if (flags)
+        WARN("Unsupported flags %#x\n", flags);
+
+    if (id != 0)
+        return MF_E_INVALIDSTREAMNUMBER;
+
+    if (!This->valid_state)
+        return MF_E_TRANSFORM_TYPE_NOT_SET;
+
+    EnterCriticalSection(&This->state_cs);
+
+    if (This->output_counter || This->draining)
+    {
+        hr = MF_E_NOTACCEPTING;
+        goto done;
+    }
+
+    if (!(gst_buffer = gst_buffer_from_mf_sample(sample)))
+    {
+        hr = E_FAIL;
+        goto done;
+    }
+
+    ret = gst_pad_push(This->input_src, gst_buffer);
+    if (ret != GST_FLOW_OK)
+    {
+        ERR("Couldn't process input ret = %d\n", ret);
+        hr =  E_FAIL;
+        goto done;
+    }
+
+    done:
+    LeaveCriticalSection(&This->state_cs);
+    return hr;
+}
+
+static HRESULT WINAPI mf_decoder_ProcessOutput(IMFTransform *iface, DWORD flags, DWORD count,
+        MFT_OUTPUT_DATA_BUFFER *samples, DWORD *status)
+{
+    struct mf_decoder *This = impl_mf_decoder_from_IMFTransform(iface);
+    GstSample *buffer;
+    MFT_OUTPUT_DATA_BUFFER *relevant_buffer = NULL;
+
+    TRACE("%p, %#x, %u, %p, %p,\n", iface, flags, count, samples, status);
+
+    if (flags)
+    {
+        WARN("Unsupported flags %#x\n", flags);
+    }
+
+    if (!This->valid_state)
+        return MF_E_TRANSFORM_TYPE_NOT_SET;
+
+    for (unsigned int i = 0; i < count; i++)
+    {
+        MFT_OUTPUT_DATA_BUFFER *out_buffer = &samples[i];
+
+        if (out_buffer->dwStreamID != 0)
+            return MF_E_INVALIDSTREAMNUMBER;
+
+        if (relevant_buffer)
+            return MF_E_INVALIDSTREAMNUMBER;
+
+        relevant_buffer = out_buffer;
+    }
+
+    if (!relevant_buffer)
+        return S_OK;
+
+    EnterCriticalSection(&This->state_cs);
+    if (This->draining)
+    {
+        if (This->eos)
+            buffer = NULL;
+        else
+            g_signal_emit_by_name(This->appsink, "pull-sample", &buffer);
+    }
+    else
+        g_signal_emit_by_name(This->appsink, "try-pull-sample", 0, &buffer);
+    LeaveCriticalSection(&This->state_cs);
+
+    if (!buffer)
+        return MF_E_TRANSFORM_NEED_MORE_INPUT;
+    This->output_counter--;
+
+    relevant_buffer->pSample = mf_sample_from_gst_buffer(gst_sample_get_buffer(buffer));
+    gst_sample_unref(buffer);
+    relevant_buffer->dwStatus = S_OK;
+    relevant_buffer->pEvents = NULL;
+    *status = 0;
+    return S_OK;
+}
+
+static const IMFTransformVtbl mf_decoder_vtbl =
+{
+    mf_decoder_QueryInterface,
+    mf_decoder_AddRef,
+    mf_decoder_Release,
+    mf_decoder_GetStreamLimits,
+    mf_decoder_GetStreamCount,
+    mf_decoder_GetStreamIDs,
+    mf_decoder_GetInputStreamInfo,
+    mf_decoder_GetOutputStreamInfo,
+    mf_decoder_GetAttributes,
+    mf_decoder_GetInputStreamAttributes,
+    mf_decoder_GetOutputStreamAttributes,
+    mf_decoder_DeleteInputStream,
+    mf_decoder_AddInputStreams,
+    mf_decoder_GetInputAvailableType,
+    mf_decoder_GetOutputAvailableType,
+    mf_decoder_SetInputType,
+    mf_decoder_SetOutputType,
+    mf_decoder_GetInputCurrentType,
+    mf_decoder_GetOutputCurrentType,
+    mf_decoder_GetInputStatus,
+    mf_decoder_GetOutputStatus,
+    mf_decoder_SetOutputBounds,
+    mf_decoder_ProcessEvent,
+    mf_decoder_ProcessMessage,
+    mf_decoder_ProcessInput,
+    mf_decoder_ProcessOutput,
+};
+
+GstBusSyncReply watch_decoder_bus(GstBus *bus, GstMessage *message, gpointer user_data)
+{
+    struct mf_decoder *This = user_data;
+    GError *err = NULL;
+    gchar *dbg_info = NULL;
+
+    TRACE("decoder %p message type %s\n", This, GST_MESSAGE_TYPE_NAME(message));
+
+    switch (message->type)
+    {
+        case GST_MESSAGE_ERROR:
+            gst_message_parse_error(message, &err, &dbg_info);
+            ERR("%s: %s\n", GST_OBJECT_NAME(message->src), err->message);
+            ERR("%s\n", dbg_info);
+            g_error_free(err);
+            g_free(dbg_info);
+            break;
+        case GST_MESSAGE_WARNING:
+            gst_message_parse_warning(message, &err, &dbg_info);
+            WARN("%s: %s\n", GST_OBJECT_NAME(message->src), err->message);
+            WARN("%s\n", dbg_info);
+            g_error_free(err);
+            g_free(dbg_info);
+            break;
+        case GST_MESSAGE_EOS:
+            This->eos = TRUE;
+            WakeAllConditionVariable(&This->state_cv);
+            break;
+        default:
+            break;
+    }
+
+    return GST_BUS_DROP;
+}
+
+static void mf_decoder_destroy(struct mf_decoder *This)
+{
+    if (This->input_type)
+    {
+        IMFMediaType_Release(This->input_type);
+        This->input_type = NULL;
+    }
+
+    if (This->output_type)
+    {
+        IMFMediaType_Release(This->output_type);
+        This->output_type = NULL;
+    }
+
+    decoder_update_pipeline(This);
+
+    if (This->their_sink)
+        gst_object_unref(G_OBJECT(This->their_sink));
+
+    if (This->container)
+        gst_object_unref(G_OBJECT(This->container));
+
+    if (This->bus)
+        gst_object_unref(G_OBJECT(This->bus));
+
+    DeleteCriticalSection(&This->state_cs);
+
+    heap_free(This);
+}
+
+HRESULT generic_decoder_construct(REFIID riid, void **obj, enum decoder_type type)
+{
+    struct mf_decoder *This;
+    HRESULT hr = S_OK;
+
+    TRACE("%s, %p %u.\n", debugstr_guid(riid), obj, type);
+
+    if (!(This = heap_alloc_zero(sizeof(*This))))
+        return E_OUTOFMEMORY;
+    This->type = type;
+    This->video = decoder_descs[type].major_type == &MFMediaType_Video;
+
+    InitializeCriticalSection(&This->state_cs);
+    InitializeConditionVariable(&This->state_cv);
+
+    This->container = gst_bin_new(NULL);
+    This->bus = gst_bus_new();
+    gst_bus_set_sync_handler(This->bus, watch_decoder_bus_wrapper, This, NULL);
+    gst_element_set_bus(This->container, This->bus);
+
+    if (!(This->converter = gst_element_factory_make(This->video ? "videoconvert" : "audioconvert", NULL)))
+    {
+        ERR("Failed to create videoconvert\n");
+        hr = E_FAIL;
+        goto fail;
+    }
+    if (!(This->appsink = gst_element_factory_make("appsink", NULL)))
+    {
+        ERR("Failed to create appsink\n");
+        hr = E_FAIL;
+        goto fail;
+    }
+
+    gst_bin_add(GST_BIN(This->container), This->converter);
+    gst_bin_add(GST_BIN(This->container), This->appsink);
+
+    if (!(gst_element_link(This->converter, This->appsink)))
+    {
+        ERR("Failed to link converter to appsink\n");
+        hr = E_FAIL;
+        goto fail;
+    }
+
+    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(decoder_new_sample_wrapper), This);
+
+    This->process_message_callback.lpVtbl = &process_message_callback_vtbl;
+
+    This->IMFTransform_iface.lpVtbl = &mf_decoder_vtbl;
+    This->refcount = 1;
+
+    *obj = This;
+    return S_OK;
+
+    fail:
+    ERR("Failed to create Decoder MFT type %u, hr = %#x\n", type, hr);
+    mf_decoder_destroy(This);
+    return hr;
+}
+
+void perform_cb_mf_decode(struct cb_data *cbdata)
+{
+    switch (cbdata->type)
+    {
+    case ACTIVATE_PUSH_MODE:
+        {
+            struct activate_mode_data *data = &cbdata->u.activate_mode_data;
+            cbdata->u.activate_mode_data.ret = activate_push_mode(data->pad, data->parent, data->mode, data->activate);
+            break;
+        }
+    case QUERY_INPUT_SRC:
+        {
+            struct query_function_data *data = &cbdata->u.query_function_data;
+            cbdata->u.query_function_data.ret = query_input_src(data->pad, data->parent, data->query);
+            break;
+        }
+    case DECODER_NEW_SAMPLE:
+        {
+            struct new_sample_data *data = &cbdata->u.new_sample_data;
+            cbdata->u.new_sample_data.ret = decoder_new_sample(data->appsink, data->user);
+            break;
+        }
+    case WATCH_DECODER_BUS:
+        {
+            struct watch_bus_data *data = &cbdata->u.watch_bus_data;
+            cbdata->u.watch_bus_data.ret = watch_decoder_bus(data->bus, data->msg, data->user);
+            break;
+        }
+    default:
+        {
+            ERR("Wrong callback forwarder called\n");
+            return;
+        }
+    }
+}
\ No newline at end of file
diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c
index 5fbb331c89..b7bab5e572 100644
--- a/dlls/winegstreamer/mfplat.c
+++ b/dlls/winegstreamer/mfplat.c
@@ -404,6 +404,16 @@ failed:
     return hr;
 }
 
+static HRESULT h264_decoder_create(REFIID riid, void **ret)
+{
+    return generic_decoder_construct(riid, ret, DECODER_TYPE_H264);
+}
+
+static HRESULT aac_decoder_create(REFIID riid, void **ret)
+{
+    return generic_decoder_construct(riid, ret, DECODER_TYPE_AAC);
+}
+
 static HRESULT mp4_stream_handler_create(REFIID riid, void **ret)
 {
     return container_stream_handler_construct(riid, ret, SOURCE_TYPE_MPEG_4);
@@ -417,6 +427,8 @@ static const struct class_object
 class_objects[] =
 {
     { &CLSID_VideoProcessorMFT, &video_processor_create },
+    { &CLSID_CMSH264DecoderMFT, &h264_decoder_create },
+    { &CLSID_CMSAACDecMFT, &aac_decoder_create },
     { &CLSID_MPEG4ByteStreamHandler, &mp4_stream_handler_create },
 };
 
@@ -446,11 +458,140 @@ HRESULT mfplat_get_class_object(REFCLSID rclsid, REFIID riid, void **obj)
     return CLASS_E_CLASSNOTAVAILABLE;
 }
 
+struct register_type_info
+{
+    const GUID *major_type;
+    const GUID *sub_type;
+};
+
+static WCHAR h264decoderW[] = {'H','.','2','6','4',' ','D','e','c','o','d','e','r',0};
+const struct register_type_info h264_decoder_input_types[] =
+{
+    {
+        &MFMediaType_Video,
+        &MFVideoFormat_H264
+    }
+};
+const struct register_type_info h264_decoder_output_types[] =
+{
+    {
+        &MFMediaType_Video,
+        &MFVideoFormat_I420
+    },
+    {
+        &MFMediaType_Video,
+        &MFVideoFormat_IYUV
+    },
+    {
+        &MFMediaType_Video,
+        &MFVideoFormat_NV12
+    },
+    {
+        &MFMediaType_Video,
+        &MFVideoFormat_YUY2,
+    },
+    {
+        &MFMediaType_Video,
+        &MFVideoFormat_YV12,
+    }
+};
+
+static WCHAR aacdecoderW[] = {'A','A','C',' ','D','e','c','o','d','e','r',0};
+const struct register_type_info aac_decoder_input_types[] =
+{
+    {
+        &MFMediaType_Audio,
+        &MFAudioFormat_AAC
+    }
+};
+
+const struct register_type_info aac_decoder_output_types[] =
+{
+    {
+        &MFMediaType_Audio,
+        &MFAudioFormat_Float
+    }
+};
+
+static const struct mft
+{
+    const GUID *clsid;
+    const GUID *category;
+    LPWSTR name;
+    const UINT32 flags;
+    const UINT32 input_types_count;
+    const struct register_type_info *input_types;
+    const UINT32 output_types_count;
+    const struct register_type_info *output_types;
+    IMFAttributes *attributes;
+}
+mfts[] =
+{
+    {
+        &CLSID_CMSH264DecoderMFT,
+        &MFT_CATEGORY_VIDEO_DECODER,
+        h264decoderW,
+        MFT_ENUM_FLAG_SYNCMFT,
+        ARRAY_SIZE(h264_decoder_input_types),
+        h264_decoder_input_types,
+        ARRAY_SIZE(h264_decoder_output_types),
+        h264_decoder_output_types,
+        NULL
+    },
+    {
+        &CLSID_CMSAACDecMFT,
+        &MFT_CATEGORY_AUDIO_DECODER,
+        aacdecoderW,
+        MFT_ENUM_FLAG_SYNCMFT,
+        ARRAY_SIZE(aac_decoder_input_types),
+        aac_decoder_input_types,
+        ARRAY_SIZE(aac_decoder_output_types),
+        aac_decoder_output_types,
+        NULL
+    }
+};
+
+HRESULT mfplat_DllRegisterServer(void)
+{
+    HRESULT hr;
+
+    for (unsigned int i = 0; i < ARRAY_SIZE(mfts); i++)
+    {
+        const struct mft *cur = &mfts[i];
+
+        MFT_REGISTER_TYPE_INFO *input_types, *output_types;
+        input_types = heap_alloc(cur->input_types_count * sizeof(input_types[0]));
+        output_types = heap_alloc(cur->output_types_count * sizeof(output_types[0]));
+        for (unsigned int i = 0; i < cur->input_types_count; i++)
+        {
+            input_types[i].guidMajorType = *(cur->input_types[i].major_type);
+            input_types[i].guidSubtype = *(cur->input_types[i].sub_type);
+        }
+        for (unsigned int i = 0; i < cur->output_types_count; i++)
+        {
+            output_types[i].guidMajorType = *(cur->output_types[i].major_type);
+            output_types[i].guidSubtype = *(cur->output_types[i].sub_type);
+        }
+
+        hr = MFTRegister(*(cur->clsid), *(cur->category), cur->name, cur->flags, cur->input_types_count,
+                    input_types, cur->output_types_count, output_types, cur->attributes);
+
+        heap_free(input_types);
+        heap_free(output_types);
+
+        if (FAILED(hr))
+        {
+            FIXME("Failed to register MFT, hr %#x\n", hr);
+            return hr;
+        }
+    }
+    return S_OK;
+}
+
 struct aac_user_data
 {
     WORD payload_type;
     WORD profile_level_indication;
-    WORD struct_type;
     WORD reserved;
   /*BYTE audio_specific_config;*/
 };
diff --git a/dlls/winegstreamer/winegstreamer_classes.idl b/dlls/winegstreamer/winegstreamer_classes.idl
index 997a28b052..ee8090cf4a 100644
--- a/dlls/winegstreamer/winegstreamer_classes.idl
+++ b/dlls/winegstreamer/winegstreamer_classes.idl
@@ -61,3 +61,15 @@ coclass VideoProcessorMFT {}
     uuid(271c3902-6095-4c45-a22f-20091816ee9e)
 ]
 coclass MPEG4ByteStreamHandler {}
+
+[
+    threading(both),
+    uuid(62ce7e72-4c71-4d20-b15d-452831a87d9d)
+]
+coclass CMSH264DecoderMFT { }
+
+[
+    threading(both),
+    uuid(32d186a7-218f-4c75-8876-dd77273a8999)
+]
+coclass CMSAACDecMFT { }
diff --git a/include/mfidl.idl b/include/mfidl.idl
index 15a68a4253..d07852e444 100644
--- a/include/mfidl.idl
+++ b/include/mfidl.idl
@@ -1066,4 +1066,6 @@ cpp_quote("EXTERN_GUID(MF_SESSION_APPROX_EVENT_OCCURRENCE_TIME, 0x190e852f, 0x62
 cpp_quote("EXTERN_GUID(MF_PMP_SERVER_CONTEXT, 0x2f00c910, 0xd2cf, 0x4278, 0x8b, 0x6a, 0xd0, 0x77, 0xfa, 0xc3, 0xa2, 0x5f);")
 
 cpp_quote("EXTERN_GUID(CLSID_VideoProcessorMFT, 0x88753b26, 0x5b24, 0x49bd, 0xb2, 0xe7, 0xc, 0x44, 0x5c, 0x78, 0xc9, 0x82);")
-cpp_quote("EXTERN_GUID(CLSID_MPEG4ByteStreamHandler, 0x271c3902, 0x6095, 0x4c45, 0xa2, 0x2f, 0x20, 0x09, 0x18, 0x16, 0xee, 0x9e);")
\ No newline at end of file
+cpp_quote("EXTERN_GUID(CLSID_MPEG4ByteStreamHandler, 0x271c3902, 0x6095, 0x4c45, 0xa2, 0x2f, 0x20, 0x09, 0x18, 0x16, 0xee, 0x9e);")
+cpp_quote("EXTERN_GUID(CLSID_CMSH264DecoderMFT, 0x62ce7e72, 0x4c71, 0x4d20, 0xb1, 0x5d, 0x45, 0x28, 0x31, 0xa8, 0x7d, 0x9d);")
+cpp_quote("EXTERN_GUID(CLSID_CMSAACDecMFT, 0x32d186a7, 0x218f, 0x4c75, 0x88, 0x76, 0xdd, 0x77, 0x27, 0x3a, 0x89, 0x99);")
-- 
2.25.1




More information about the wine-devel mailing list