[PATCH 5/6] winegstreamer: Reimplement the media source on top of the wg_parser object.

Zebediah Figura z.figura12 at gmail.com
Thu Feb 18 17:01:27 CST 2021


Signed-off-by: Zebediah Figura <z.figura12 at gmail.com>
---
 dlls/winegstreamer/Makefile.in    |   1 -
 dlls/winegstreamer/audioconvert.c |  12 -
 dlls/winegstreamer/gst_cbs.c      | 235 ----------
 dlls/winegstreamer/gst_cbs.h      | 110 -----
 dlls/winegstreamer/gst_private.h  |   8 +-
 dlls/winegstreamer/gstdemux.c     |  19 -
 dlls/winegstreamer/main.c         |   2 -
 dlls/winegstreamer/media_source.c | 699 +++++++-----------------------
 dlls/winegstreamer/mfplat.c       | 460 ++++++++------------
 dlls/winegstreamer/wg_parser.c    |  12 +-
 10 files changed, 337 insertions(+), 1221 deletions(-)
 delete mode 100644 dlls/winegstreamer/gst_cbs.c
 delete mode 100644 dlls/winegstreamer/gst_cbs.h

diff --git a/dlls/winegstreamer/Makefile.in b/dlls/winegstreamer/Makefile.in
index c5e4ed1ef42..debdae2d4bb 100644
--- a/dlls/winegstreamer/Makefile.in
+++ b/dlls/winegstreamer/Makefile.in
@@ -8,7 +8,6 @@ PARENTSRC = ../strmbase
 C_SRCS = \
 	audioconvert.c \
 	filter.c \
-	gst_cbs.c \
 	gstdemux.c \
 	main.c \
 	media_source.c \
diff --git a/dlls/winegstreamer/audioconvert.c b/dlls/winegstreamer/audioconvert.c
index 85f44dd8856..f4e841fe3e4 100644
--- a/dlls/winegstreamer/audioconvert.c
+++ b/dlls/winegstreamer/audioconvert.c
@@ -277,7 +277,6 @@ fail:
 static HRESULT WINAPI audio_converter_SetInputType(IMFTransform *iface, DWORD id, IMFMediaType *type, DWORD flags)
 {
     GUID major_type, subtype;
-    GstCaps *input_caps;
     DWORD unused;
     HRESULT hr;
 
@@ -323,11 +322,6 @@ static HRESULT WINAPI audio_converter_SetInputType(IMFTransform *iface, DWORD id
     if (!IsEqualGUID(&subtype, &MFAudioFormat_PCM) && !IsEqualGUID(&subtype, &MFAudioFormat_Float))
         return MF_E_INVALIDTYPE;
 
-    if (!(input_caps = caps_from_mf_media_type(type)))
-        return MF_E_INVALIDTYPE;
-
-    gst_caps_unref(input_caps);
-
     if (flags & MFT_SET_TYPE_TEST_ONLY)
         return S_OK;
 
@@ -356,7 +350,6 @@ static HRESULT WINAPI audio_converter_SetOutputType(IMFTransform *iface, DWORD i
 {
     struct audio_converter *converter = impl_audio_converter_from_IMFTransform(iface);
     GUID major_type, subtype;
-    GstCaps *output_caps;
     DWORD unused;
     HRESULT hr;
 
@@ -403,11 +396,6 @@ static HRESULT WINAPI audio_converter_SetOutputType(IMFTransform *iface, DWORD i
     if (!IsEqualGUID(&subtype, &MFAudioFormat_PCM) && !IsEqualGUID(&subtype, &MFAudioFormat_Float))
         return MF_E_INVALIDTYPE;
 
-    if (!(output_caps = caps_from_mf_media_type(type)))
-        return MF_E_INVALIDTYPE;
-
-    gst_caps_unref(output_caps);
-
     if (flags & MFT_SET_TYPE_TEST_ONLY)
         return S_OK;
 
diff --git a/dlls/winegstreamer/gst_cbs.c b/dlls/winegstreamer/gst_cbs.c
deleted file mode 100644
index d037c195971..00000000000
--- a/dlls/winegstreamer/gst_cbs.c
+++ /dev/null
@@ -1,235 +0,0 @@
-/*
- * Copyright 2015 Andrew Eikum for CodeWeavers
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
- */
-
-#include "config.h"
-
-#include <gst/gst.h>
-
-#include "objbase.h"
-
-#include "wine/list.h"
-
-#include "gst_cbs.h"
-
-static pthread_key_t wine_gst_key;
-
-void mark_wine_thread(void)
-{
-    /* set it to non-NULL to indicate that this is a Wine thread */
-    pthread_setspecific(wine_gst_key, &wine_gst_key);
-}
-
-static BOOL is_wine_thread(void)
-{
-    return pthread_getspecific(wine_gst_key) != NULL;
-}
-
-static pthread_mutex_t cb_list_lock = PTHREAD_MUTEX_INITIALIZER;
-static pthread_cond_t cb_list_cond = PTHREAD_COND_INITIALIZER;
-static struct list cb_list = LIST_INIT(cb_list);
-
-static void CALLBACK perform_cb(TP_CALLBACK_INSTANCE *instance, void *user)
-{
-    struct cb_data *cbdata = user;
-
-    perform_cb_media_source(cbdata);
-
-    pthread_mutex_lock(&cbdata->lock);
-    cbdata->finished = 1;
-    pthread_cond_broadcast(&cbdata->cond);
-    pthread_mutex_unlock(&cbdata->lock);
-}
-
-static DWORD WINAPI dispatch_thread(void *user)
-{
-    struct cb_data *cbdata;
-
-    CoInitializeEx(NULL, COINIT_MULTITHREADED);
-
-    pthread_mutex_lock(&cb_list_lock);
-
-    while (1)
-    {
-        while (list_empty(&cb_list)) pthread_cond_wait(&cb_list_cond, &cb_list_lock);
-
-        cbdata = LIST_ENTRY(list_head(&cb_list), struct cb_data, entry);
-        list_remove(&cbdata->entry);
-        TrySubmitThreadpoolCallback(&perform_cb, cbdata, NULL);
-    }
-
-    pthread_mutex_unlock(&cb_list_lock);
-
-    CoUninitialize();
-
-    return 0;
-}
-
-void start_dispatch_thread(void)
-{
-    pthread_key_create(&wine_gst_key, NULL);
-    CloseHandle(CreateThread(NULL, 0, &dispatch_thread, NULL, 0, NULL));
-}
-
-/* gstreamer calls our callbacks from threads that Wine did not create. Some
- * callbacks execute code which requires Wine to have created the thread
- * (critical sections, debug logging, dshow client code). Since gstreamer can't
- * provide an API to override its thread creation, we have to intercept all
- * callbacks in code which avoids the Wine thread requirement, and then
- * dispatch those callbacks on a thread that is known to be created by Wine.
- *
- * This thread must not run any code that depends on the Wine TEB!
- */
-
-static void call_cb(struct cb_data *cbdata)
-{
-    pthread_mutex_init(&cbdata->lock, NULL);
-    pthread_cond_init(&cbdata->cond, NULL);
-    cbdata->finished = 0;
-
-    if(is_wine_thread()){
-        /* The thread which triggered gstreamer to call this callback may
-         * already hold a critical section. If so, executing the callback on a
-         * worker thread can cause a deadlock. If  we are already on a Wine
-         * thread, then there is no need to run this callback on a worker
-         * thread anyway, which avoids the deadlock issue. */
-        perform_cb(NULL, cbdata);
-
-        pthread_cond_destroy(&cbdata->cond);
-        pthread_mutex_destroy(&cbdata->lock);
-
-        return;
-    }
-
-    pthread_mutex_lock(&cb_list_lock);
-
-    list_add_tail(&cb_list, &cbdata->entry);
-    pthread_cond_broadcast(&cb_list_cond);
-
-    pthread_mutex_lock(&cbdata->lock);
-
-    pthread_mutex_unlock(&cb_list_lock);
-
-    while(!cbdata->finished)
-        pthread_cond_wait(&cbdata->cond, &cbdata->lock);
-
-    pthread_mutex_unlock(&cbdata->lock);
-
-    pthread_cond_destroy(&cbdata->cond);
-    pthread_mutex_destroy(&cbdata->lock);
-}
-
-GstFlowReturn bytestream_wrapper_pull_wrapper(GstPad *pad, GstObject *parent, guint64 ofs, guint len,
-        GstBuffer **buf)
-{
-    struct cb_data cbdata = { BYTESTREAM_WRAPPER_PULL };
-
-    cbdata.u.getrange_data.pad = pad;
-    cbdata.u.getrange_data.parent = parent;
-    cbdata.u.getrange_data.ofs = ofs;
-    cbdata.u.getrange_data.len = len;
-    cbdata.u.getrange_data.buf = buf;
-
-    call_cb(&cbdata);
-
-    return cbdata.u.getrange_data.ret;
-}
-
-gboolean bytestream_query_wrapper(GstPad *pad, GstObject *parent, GstQuery *query)
-{
-    struct cb_data cbdata = { BYTESTREAM_QUERY };
-
-    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;
-}
-
-gboolean bytestream_pad_mode_activate_wrapper(GstPad *pad, GstObject *parent, GstPadMode mode, gboolean activate)
-{
-    struct cb_data cbdata = { BYTESTREAM_PAD_MODE_ACTIVATE };
-
-    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.activate_mode_data.ret;
-}
-
-gboolean bytestream_pad_event_process_wrapper(GstPad *pad, GstObject *parent, GstEvent *event)
-{
-    struct cb_data cbdata = { BYTESTREAM_PAD_EVENT_PROCESS };
-
-    cbdata.u.event_src_data.pad = pad;
-    cbdata.u.event_src_data.parent = parent;
-    cbdata.u.event_src_data.event = event;
-
-    call_cb(&cbdata);
-
-    return cbdata.u.event_src_data.ret;
-}
-
-GstBusSyncReply mf_src_bus_watch_wrapper(GstBus *bus, GstMessage *message, gpointer user)
-{
-    struct cb_data cbdata = { MF_SRC_BUS_WATCH };
-
-    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 mf_src_stream_added_wrapper(GstElement *bin, GstPad *pad, gpointer user)
-{
-    struct cb_data cbdata = { MF_SRC_STREAM_ADDED };
-
-    cbdata.u.pad_added_data.element = bin;
-    cbdata.u.pad_added_data.pad = pad;
-    cbdata.u.pad_added_data.user = user;
-
-    call_cb(&cbdata);
-}
-
-void mf_src_stream_removed_wrapper(GstElement *element, GstPad *pad, gpointer user)
-{
-    struct cb_data cbdata = { MF_SRC_STREAM_REMOVED };
-
-    cbdata.u.pad_removed_data.element = element;
-    cbdata.u.pad_removed_data.pad = pad;
-    cbdata.u.pad_removed_data.user = user;
-
-    call_cb(&cbdata);
-}
-
-void mf_src_no_more_pads_wrapper(GstElement *element, gpointer user)
-{
-    struct cb_data cbdata = { MF_SRC_NO_MORE_PADS };
-
-    cbdata.u.no_more_pads_data.element = element;
-    cbdata.u.no_more_pads_data.user = user;
-
-    call_cb(&cbdata);
-}
diff --git a/dlls/winegstreamer/gst_cbs.h b/dlls/winegstreamer/gst_cbs.h
deleted file mode 100644
index 1daebee7906..00000000000
--- a/dlls/winegstreamer/gst_cbs.h
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright 2015 Andrew Eikum for CodeWeavers
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
- */
-
-#ifndef GST_CBS_H
-#define GST_CBS_H
-
-#include "wine/list.h"
-#include "windef.h"
-#include <pthread.h>
-
-enum CB_TYPE {
-    BYTESTREAM_WRAPPER_PULL,
-    BYTESTREAM_QUERY,
-    BYTESTREAM_PAD_MODE_ACTIVATE,
-    BYTESTREAM_PAD_EVENT_PROCESS,
-    MF_SRC_BUS_WATCH,
-    MF_SRC_STREAM_ADDED,
-    MF_SRC_STREAM_REMOVED,
-    MF_SRC_NO_MORE_PADS,
-    MEDIA_SOURCE_MAX,
-};
-
-struct cb_data {
-    enum CB_TYPE type;
-    union {
-        struct watch_bus_data {
-            GstBus *bus;
-            GstMessage *msg;
-            gpointer user;
-            GstBusSyncReply ret;
-        } watch_bus_data;
-        struct pad_added_data {
-            GstElement *element;
-            GstPad *pad;
-            gpointer user;
-        } pad_added_data;
-        struct query_function_data {
-            GstPad *pad;
-            GstObject *parent;
-            GstQuery *query;
-            gboolean ret;
-        } query_function_data;
-        struct activate_mode_data {
-            GstPad *pad;
-            GstObject *parent;
-            GstPadMode mode;
-            gboolean activate;
-            gboolean ret;
-        } activate_mode_data;
-        struct no_more_pads_data {
-            GstElement *element;
-            gpointer user;
-        } no_more_pads_data;
-        struct getrange_data {
-            GstPad *pad;
-            GstObject *parent;
-            guint64 ofs;
-            guint len;
-            GstBuffer **buf;
-            GstFlowReturn ret;
-        } getrange_data;
-        struct event_src_data {
-            GstPad *pad;
-            GstObject *parent;
-            GstEvent *event;
-            gboolean ret;
-        } event_src_data;
-        struct pad_removed_data {
-            GstElement *element;
-            GstPad *pad;
-            gpointer user;
-        } pad_removed_data;
-    } u;
-
-    int finished;
-    pthread_mutex_t lock;
-    pthread_cond_t cond;
-    struct list entry;
-};
-
-void mark_wine_thread(void) DECLSPEC_HIDDEN;
-void perform_cb_media_source(struct cb_data *data) DECLSPEC_HIDDEN;
-
-GstFlowReturn got_data_wrapper(GstPad *pad, GstObject *parent, GstBuffer *buf) DECLSPEC_HIDDEN;
-void Gstreamer_transform_pad_added_wrapper(GstElement *filter, GstPad *pad, gpointer user) DECLSPEC_HIDDEN;
-GstFlowReturn bytestream_wrapper_pull_wrapper(GstPad *pad, GstObject *parent, guint64 ofs, guint len, GstBuffer **buf) DECLSPEC_HIDDEN;
-gboolean bytestream_query_wrapper(GstPad *pad, GstObject *parent, GstQuery *query) DECLSPEC_HIDDEN;
-gboolean bytestream_pad_mode_activate_wrapper(GstPad *pad, GstObject *parent, GstPadMode mode, gboolean activate) DECLSPEC_HIDDEN;
-gboolean bytestream_pad_event_process_wrapper(GstPad *pad, GstObject *parent, GstEvent *event) DECLSPEC_HIDDEN;
-GstBusSyncReply mf_src_bus_watch_wrapper(GstBus *bus, GstMessage *message, gpointer user) DECLSPEC_HIDDEN;
-void mf_src_stream_added_wrapper(GstElement *bin, GstPad *pad, gpointer user) DECLSPEC_HIDDEN;
-void mf_src_stream_removed_wrapper(GstElement *element, GstPad *pad, gpointer user) DECLSPEC_HIDDEN;
-void mf_src_no_more_pads_wrapper(GstElement *element, gpointer user) DECLSPEC_HIDDEN;
-
-#endif
diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h
index 128546a0265..03d19af1031 100644
--- a/dlls/winegstreamer/gst_private.h
+++ b/dlls/winegstreamer/gst_private.h
@@ -180,6 +180,8 @@ struct unix_funcs
             void **data, uint64_t *offset, uint32_t *size);
     void (CDECL *wg_parser_complete_read_request)(struct wg_parser *parser, bool ret);
 
+    void (CDECL *wg_parser_set_unlimited_buffering)(struct wg_parser *parser);
+
     uint32_t (CDECL *wg_parser_get_stream_count)(struct wg_parser *parser);
     struct wg_parser_stream *(CDECL *wg_parser_get_stream)(struct wg_parser *parser, uint32_t index);
 
@@ -217,10 +219,8 @@ void start_dispatch_thread(void) DECLSPEC_HIDDEN;
 extern HRESULT mfplat_get_class_object(REFCLSID rclsid, REFIID riid, void **obj) DECLSPEC_HIDDEN;
 extern HRESULT mfplat_DllRegisterServer(void) DECLSPEC_HIDDEN;
 
-HRESULT winegstreamer_stream_handler_create(REFIID riid, void **obj) DECLSPEC_HIDDEN;
-IMFMediaType *mf_media_type_from_caps(const GstCaps *caps) DECLSPEC_HIDDEN;
-GstCaps *caps_from_mf_media_type(IMFMediaType *type) DECLSPEC_HIDDEN;
-IMFSample *mf_sample_from_gst_buffer(GstBuffer *in) DECLSPEC_HIDDEN;
+IMFMediaType *mf_media_type_from_wg_format(const struct wg_format *format) DECLSPEC_HIDDEN;
+void mf_media_type_to_wg_format(IMFMediaType *type, struct wg_format *format) DECLSPEC_HIDDEN;
 
 HRESULT winegstreamer_stream_handler_create(REFIID riid, void **obj) DECLSPEC_HIDDEN;
 
diff --git a/dlls/winegstreamer/gstdemux.c b/dlls/winegstreamer/gstdemux.c
index 75b1cece095..910abada48c 100644
--- a/dlls/winegstreamer/gstdemux.c
+++ b/dlls/winegstreamer/gstdemux.c
@@ -23,7 +23,6 @@
 #include "config.h"
 #include "gst_private.h"
 #include "gst_guids.h"
-#include "gst_cbs.h"
 
 #include "vfwmsgs.h"
 #include "amvideo.h"
@@ -959,8 +958,6 @@ static HRESULT parser_sink_connect(struct strmbase_sink *iface, IPin *peer, cons
     LONGLONG unused;
     unsigned int i;
 
-    mark_wine_thread();
-
     filter->reader = NULL;
     if (FAILED(hr = IPin_QueryInterface(peer, &IID_IAsyncReader, (void **)&filter->reader)))
         return hr;
@@ -996,8 +993,6 @@ static void parser_sink_disconnect(struct strmbase_sink *iface)
 {
     struct parser *filter = impl_from_strmbase_sink(iface);
 
-    mark_wine_thread();
-
     GST_RemoveOutputPins(filter);
 
     IAsyncReader_Release(filter->reader);
@@ -1104,8 +1099,6 @@ HRESULT decodebin_parser_create(IUnknown *outer, IUnknown **out)
     if (!parser_init_gstreamer())
         return E_FAIL;
 
-    mark_wine_thread();
-
     if (!(object = heap_alloc_zero(sizeof(*object))))
         return E_OUTOFMEMORY;
 
@@ -1199,7 +1192,6 @@ static HRESULT WINAPI GST_ChangeRate(IMediaSeeking *iface)
 {
     struct parser_source *pin = impl_from_IMediaSeeking(iface);
 
-    mark_wine_thread();
     unix_funcs->wg_parser_stream_seek(pin->wg_stream, pin->seek.dRate, 0, 0,
             AM_SEEKING_NoPositioning, AM_SEEKING_NoPositioning);
     return S_OK;
@@ -1235,8 +1227,6 @@ static HRESULT WINAPI GST_Seeking_SetPositions(IMediaSeeking *iface,
             pin, current ? debugstr_time(*current) : "<null>", current_flags,
             stop ? debugstr_time(*stop) : "<null>", stop_flags);
 
-    mark_wine_thread();
-
     if (pin->pin.pin.filter->state == State_Stopped)
     {
         SourceSeekingImpl_SetPositions(iface, current, current_flags, stop, stop_flags);
@@ -1356,8 +1346,6 @@ static HRESULT WINAPI GST_QualityControl_Notify(IQualityControl *iface, IBaseFil
             pin, sender, q.Type == Famine ? "Famine" : "Flood", q.Proportion,
             debugstr_time(q.Late), debugstr_time(q.TimeStamp));
 
-    mark_wine_thread();
-
     /* DirectShow filters sometimes pass negative timestamps (Audiosurf uses the
      * current time instead of the time of the last buffer). GstClockTime is
      * unsigned, so clamp it to 0. */
@@ -1545,7 +1533,6 @@ static HRESULT GST_RemoveOutputPins(struct parser *This)
     unsigned int i;
 
     TRACE("(%p)\n", This);
-    mark_wine_thread();
 
     if (!This->sink_connected)
         return S_OK;
@@ -1645,8 +1632,6 @@ HRESULT wave_parser_create(IUnknown *outer, IUnknown **out)
     if (!parser_init_gstreamer())
         return E_FAIL;
 
-    mark_wine_thread();
-
     if (!(object = heap_alloc_zero(sizeof(*object))))
         return E_OUTOFMEMORY;
 
@@ -1735,8 +1720,6 @@ HRESULT avi_splitter_create(IUnknown *outer, IUnknown **out)
     if (!parser_init_gstreamer())
         return E_FAIL;
 
-    mark_wine_thread();
-
     if (!(object = heap_alloc_zero(sizeof(*object))))
         return E_OUTOFMEMORY;
 
@@ -1846,8 +1829,6 @@ HRESULT mpeg_splitter_create(IUnknown *outer, IUnknown **out)
     if (!parser_init_gstreamer())
         return E_FAIL;
 
-    mark_wine_thread();
-
     if (!(object = heap_alloc_zero(sizeof(*object))))
         return E_OUTOFMEMORY;
 
diff --git a/dlls/winegstreamer/main.c b/dlls/winegstreamer/main.c
index 4e46a99f513..ee9f57b6c57 100644
--- a/dlls/winegstreamer/main.c
+++ b/dlls/winegstreamer/main.c
@@ -183,8 +183,6 @@ static BOOL CALLBACK init_gstreamer_proc(INIT_ONCE *once, void *param, void **ct
     if (!handle)
         ERR("Failed to pin module %p.\n", winegstreamer_instance);
 
-    start_dispatch_thread();
-
     return TRUE;
 }
 
diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c
index 604e0ef056c..32b4477ad88 100644
--- a/dlls/winegstreamer/media_source.c
+++ b/dlls/winegstreamer/media_source.c
@@ -1,6 +1,7 @@
 /* GStreamer Media Source
  *
  * Copyright 2020 Derek Lesho
+ * Copyright 2020 Zebediah Figura for CodeWeavers
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -22,7 +23,6 @@
 #include <gst/gst.h>
 
 #include "gst_private.h"
-#include "gst_cbs.h"
 
 #include <assert.h>
 #include <stdarg.h>
@@ -49,8 +49,9 @@ struct media_stream
     struct media_source *parent_source;
     IMFMediaEventQueue *event_queue;
     IMFStreamDescriptor *descriptor;
-    GstElement *appsink;
-    GstPad *their_src, *my_sink;
+
+    struct wg_parser_stream *wg_stream;
+
     enum
     {
         STREAM_INACTIVE,
@@ -97,13 +98,12 @@ struct media_source
     DWORD async_commands_queue;
     IMFMediaEventQueue *event_queue;
     IMFByteStream *byte_stream;
+
+    struct wg_parser *wg_parser;
+
     struct media_stream **streams;
     ULONG stream_count;
     IMFPresentationDescriptor *pres_desc;
-    GstBus *bus;
-    GstElement *container;
-    GstElement *decodebin;
-    GstPad *my_src, *their_sink;
     enum
     {
         SOURCE_OPENING,
@@ -111,24 +111,11 @@ struct media_source
         SOURCE_RUNNING,
         SOURCE_SHUTDOWN,
     } state;
-    HANDLE no_more_pads_event;
 
-    uint64_t file_size, next_pull_offset;
+    LONGLONG start_time;
 
     HANDLE read_thread;
     bool read_thread_shutdown;
-
-    pthread_mutex_t mutex;
-    pthread_cond_t read_cond, read_done_cond;
-    struct
-    {
-        void *data;
-        uint64_t offset;
-        uint32_t size;
-        bool done;
-        bool ret;
-    } read_request;
-    bool shutdown;
 };
 
 static inline struct media_stream *impl_from_IMFMediaStream(IMFMediaStream *iface)
@@ -271,19 +258,15 @@ static void start_pipeline(struct media_source *source, struct source_async_comm
 {
     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;
     }
+    source->start_time = position->u.hVal.QuadPart;
 
     for (i = 0; i < source->stream_count; i++)
     {
@@ -291,8 +274,6 @@ static void start_pipeline(struct media_source *source, struct source_async_comm
         IMFStreamDescriptor *sd;
         IMFMediaTypeHandler *mth;
         IMFMediaType *current_mt;
-        GstCaps *current_caps;
-        GstCaps *prev_caps;
         DWORD stream_id;
         BOOL was_active;
         BOOL selected;
@@ -310,35 +291,20 @@ static void start_pipeline(struct media_source *source, struct source_async_comm
 
         if (selected)
         {
+            struct wg_format format;
+
             IMFStreamDescriptor_GetMediaTypeHandler(stream->descriptor, &mth);
             IMFMediaTypeHandler_GetCurrentMediaType(mth, &current_mt);
-            current_caps = caps_from_mf_media_type(current_mt);
-            g_object_get(stream->appsink, "caps", &prev_caps, NULL);
-            if (!prev_caps || !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);
-            if (prev_caps)
-                gst_caps_unref(prev_caps);
+            mf_media_type_to_wg_format(current_mt, &format);
+            unix_funcs->wg_parser_stream_enable(stream->wg_stream, &format);
+
             IMFMediaType_Release(current_mt);
             IMFMediaTypeHandler_Release(mth);
         }
 
-        g_object_set(stream->appsink, "drop", !selected, NULL);
-
         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);
-
-            gst_pad_push_event(stream->my_sink, seek_event);
-
             stream->eos = FALSE;
-        }
 
         if (selected)
         {
@@ -358,20 +324,25 @@ static void start_pipeline(struct media_source *source, struct source_async_comm
 
     source->state = SOURCE_RUNNING;
 
-    gst_element_set_state(source->container, GST_STATE_PLAYING);
+    unix_funcs->wg_parser_stream_seek(source->streams[0]->wg_stream, 1.0,
+            position->u.hVal.QuadPart, 0, AM_SEEKING_AbsolutePositioning, AM_SEEKING_NoPositioning);
+    unix_funcs->wg_parser_end_flush(source->wg_parser);
 }
 
 static void stop_pipeline(struct media_source *source)
 {
     unsigned int i;
 
-    gst_element_set_state(source->container, GST_STATE_PAUSED);
+    unix_funcs->wg_parser_begin_flush(source->wg_parser);
 
     for (i = 0; i < source->stream_count; i++)
     {
         struct media_stream *stream = source->streams[i];
         if (stream->state != STREAM_INACTIVE)
+        {
             IMFMediaEventQueue_QueueEventParamVar(stream->event_queue, MEStreamStopped, &GUID_NULL, S_OK, NULL);
+            unix_funcs->wg_parser_stream_disable(stream->wg_stream);
+        }
     }
 
     IMFMediaEventQueue_QueueEventParamVar(source->event_queue, MESourceStopped, &GUID_NULL, S_OK, NULL);
@@ -396,40 +367,114 @@ static void dispatch_end_of_presentation(struct media_source *source)
     IMFMediaEventQueue_QueueEventParamVar(source->event_queue, MEEndOfPresentation, &GUID_NULL, S_OK, &empty);
 }
 
-static void wait_on_sample(struct media_stream *stream, IUnknown *token)
+static void send_buffer(struct media_stream *stream, const struct wg_parser_event *event, IUnknown *token)
 {
-    PROPVARIANT empty_var = {.vt = VT_EMPTY};
-    GstSample *gst_sample;
-    GstBuffer *buffer;
+    IMFMediaBuffer *buffer;
     IMFSample *sample;
+    HRESULT hr;
+    BYTE *data;
 
-    TRACE("%p, %p\n", stream, token);
+    if (FAILED(hr = MFCreateSample(&sample)))
+    {
+        ERR("Failed to create sample, hr %#x.\n", hr);
+        return;
+    }
+
+    if (FAILED(hr = MFCreateMemoryBuffer(event->u.buffer.size, &buffer)))
+    {
+        ERR("Failed to create buffer, hr %#x.\n", hr);
+        IMFSample_Release(sample);
+        return;
+    }
 
-    g_signal_emit_by_name(stream->appsink, "pull-sample", &gst_sample);
-    if (gst_sample)
+    if (FAILED(hr = IMFSample_AddBuffer(sample, buffer)))
     {
-        buffer = gst_sample_get_buffer(gst_sample);
+        ERR("Failed to add buffer, hr %#x.\n", hr);
+        goto out;
+    }
 
-        TRACE("PTS = %llu\n", (unsigned long long int) GST_BUFFER_PTS(buffer));
+    if (FAILED(hr = IMFMediaBuffer_SetCurrentLength(buffer, event->u.buffer.size)))
+    {
+        ERR("Failed to set size, hr %#x.\n", hr);
+        goto out;
+    }
 
-        sample = mf_sample_from_gst_buffer(buffer);
-        gst_sample_unref(gst_sample);
+    if (FAILED(hr = IMFMediaBuffer_Lock(buffer, &data, NULL, NULL)))
+    {
+        ERR("Failed to lock buffer, hr %#x.\n", hr);
+        goto out;
+    }
 
-        if (token)
-            IMFSample_SetUnknown(sample, &MFSampleExtension_Token, token);
+    if (!unix_funcs->wg_parser_stream_copy_buffer(stream->wg_stream, data, 0, event->u.buffer.size))
+    {
+        unix_funcs->wg_parser_stream_release_buffer(stream->wg_stream);
+        IMFMediaBuffer_Unlock(buffer);
+        goto out;
+    }
+    unix_funcs->wg_parser_stream_release_buffer(stream->wg_stream);
 
-        IMFMediaEventQueue_QueueEventParamUnk(stream->event_queue, MEMediaSample, &GUID_NULL, S_OK, (IUnknown *)sample);
-        IMFSample_Release(sample);
+    if (FAILED(hr = IMFMediaBuffer_Unlock(buffer)))
+    {
+        ERR("Failed to unlock buffer, hr %#x.\n", hr);
+        goto out;
     }
-    else
+
+    if (FAILED(hr = IMFSample_SetSampleTime(sample, event->u.buffer.pts - stream->parent_source->start_time)))
+    {
+        ERR("Failed to set sample time, hr %#x.\n", hr);
+        goto out;
+    }
+
+    if (FAILED(hr = IMFSample_SetSampleDuration(sample, event->u.buffer.duration)))
     {
-        g_object_get(stream->appsink, "eos", &stream->eos, NULL);
-        if (stream->eos)
+        ERR("Failed to set sample duration, hr %#x.\n", hr);
+        goto out;
+    }
+
+    if (token)
+        IMFSample_SetUnknown(sample, &MFSampleExtension_Token, token);
+
+    IMFMediaEventQueue_QueueEventParamUnk(stream->event_queue, MEMediaSample,
+            &GUID_NULL, S_OK, (IUnknown *)sample);
+
+out:
+    IMFMediaBuffer_Release(buffer);
+    IMFSample_Release(sample);
+}
+
+static void wait_on_sample(struct media_stream *stream, IUnknown *token)
+{
+    PROPVARIANT empty_var = {.vt = VT_EMPTY};
+    struct wg_parser_event event;
+
+    TRACE("%p, %p\n", stream, token);
+
+    for (;;)
+    {
+        if (!unix_funcs->wg_parser_stream_get_event(stream->wg_stream, &event))
+            return;
+
+        TRACE("Got event of type %#x.\n", event.type);
+
+        switch (event.type)
         {
-            if (token)
-                IUnknown_Release(token);
-            IMFMediaEventQueue_QueueEventParamVar(stream->event_queue, MEEndOfStream, &GUID_NULL, S_OK, &empty_var);
-            dispatch_end_of_presentation(stream->parent_source);
+            case WG_PARSER_EVENT_BUFFER:
+                send_buffer(stream, &event, token);
+                return;
+
+            case WG_PARSER_EVENT_EOS:
+                stream->eos = TRUE;
+                if (token)
+                    IUnknown_Release(token);
+                IMFMediaEventQueue_QueueEventParamVar(stream->event_queue, MEEndOfStream, &GUID_NULL, S_OK, &empty_var);
+                dispatch_end_of_presentation(stream->parent_source);
+                return;
+
+            case WG_PARSER_EVENT_SEGMENT:
+                break;
+
+            case WG_PARSER_EVENT_NONE:
+                assert(0);
         }
     }
 }
@@ -475,86 +520,6 @@ static const IMFAsyncCallbackVtbl source_async_commands_callback_vtbl =
     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);
-    GstBuffer *new_buffer = NULL;
-    GstMapInfo info;
-    bool ret;
-
-    TRACE("requesting %u bytes at %s from source %p into buffer %p\n", len, wine_dbgstr_longlong(ofs), source, *buf);
-
-    if (ofs == GST_BUFFER_OFFSET_NONE)
-        ofs = source->next_pull_offset;
-    source->next_pull_offset = ofs + len;
-    if (ofs >= source->file_size)
-        return GST_FLOW_EOS;
-    if (ofs + len >= source->file_size)
-        len = source->file_size - ofs;
-
-    if (!(*buf))
-        *buf = new_buffer = gst_buffer_new_and_alloc(len);
-    gst_buffer_map(*buf, &info, GST_MAP_WRITE);
-
-    pthread_mutex_lock(&source->mutex);
-
-    assert(!source->read_request.data);
-    source->read_request.data = info.data;
-    source->read_request.offset = ofs;
-    source->read_request.size = len;
-    source->read_request.done = false;
-    pthread_cond_signal(&source->read_cond);
-
-    /* Note that we don't unblock this wait on GST_EVENT_FLUSH_START. We expect
-     * the upstream pin to flush if necessary. We should never be blocked on
-     * read_thread() not running. */
-
-    while (!source->read_request.done)
-        pthread_cond_wait(&source->read_done_cond, &source->mutex);
-
-    ret = source->read_request.ret;
-
-    pthread_mutex_unlock(&source->mutex);
-
-    gst_buffer_unmap(*buf, &info);
-
-    if (!ret && new_buffer)
-        gst_buffer_unref(new_buffer);
-    return ret ? GST_FLOW_OK : GST_FLOW_ERROR;
-}
-
-static bool get_read_request(struct media_source *source, void **data, uint64_t *offset, uint32_t *size)
-{
-    pthread_mutex_lock(&source->mutex);
-
-    while (!source->shutdown && !source->read_request.data)
-        pthread_cond_wait(&source->read_cond, &source->mutex);
-
-    if (source->shutdown)
-    {
-        pthread_mutex_unlock(&source->mutex);
-        return false;
-    }
-
-    *data = source->read_request.data;
-    *offset = source->read_request.offset;
-    *size = source->read_request.size;
-
-    pthread_mutex_unlock(&source->mutex);
-    return true;
-}
-
-static void complete_read_request(struct media_source *source, bool ret)
-{
-    pthread_mutex_lock(&source->mutex);
-    source->read_request.done = true;
-    source->read_request.ret = ret;
-    source->read_request.data = NULL;
-    pthread_mutex_unlock(&source->mutex);
-    pthread_cond_signal(&source->read_done_cond);
-}
-
 static DWORD CALLBACK read_thread(void *arg)
 {
     struct media_source *source = arg;
@@ -570,137 +535,20 @@ static DWORD CALLBACK read_thread(void *arg)
         HRESULT hr;
         void *data;
 
-        if (!get_read_request(source, &data, &offset, &size))
+        if (!unix_funcs->wg_parser_get_read_request(source->wg_parser, &data, &offset, &size))
             continue;
 
         if (SUCCEEDED(hr = IMFByteStream_SetCurrentPosition(byte_stream, offset)))
             hr = IMFByteStream_Read(byte_stream, data, size, &ret_size);
         if (SUCCEEDED(hr) && ret_size != size)
             ERR("Unexpected short read: requested %u bytes, got %u.\n", size, ret_size);
-        complete_read_request(source, SUCCEEDED(hr));
+        unix_funcs->wg_parser_complete_read_request(source->wg_parser, SUCCEEDED(hr));
     }
 
     TRACE("Media source is shutting down; exiting.\n");
     return 0;
 }
 
-static gboolean bytestream_query(GstPad *pad, GstObject *parent, GstQuery *query)
-{
-    struct media_source *source = gst_pad_get_element_private(pad);
-    GstFormat format;
-    QWORD bytestream_len;
-
-    TRACE("GStreamer queries source %p for %s\n", source, GST_QUERY_TYPE_NAME(query));
-
-    if (FAILED(IMFByteStream_GetLength(source->byte_stream, &bytestream_len)))
-        return FALSE;
-
-    switch (GST_QUERY_TYPE(query))
-    {
-        case GST_QUERY_DURATION:
-        {
-            gst_query_parse_duration(query, &format, NULL);
-            if (format == GST_FORMAT_PERCENT)
-            {
-                gst_query_set_duration(query, GST_FORMAT_PERCENT, GST_FORMAT_PERCENT_MAX);
-                return TRUE;
-            }
-            else if (format == GST_FORMAT_BYTES)
-            {
-                QWORD length;
-                IMFByteStream_GetLength(source->byte_stream, &length);
-                gst_query_set_duration(query, GST_FORMAT_BYTES, length);
-                return TRUE;
-            }
-            return FALSE;
-        }
-        case GST_QUERY_SEEKING:
-        {
-            gst_query_parse_seeking (query, &format, NULL, NULL, NULL);
-            if (format != GST_FORMAT_BYTES)
-            {
-                WARN("Cannot seek using format \"%s\".\n", gst_format_get_name(format));
-                return FALSE;
-            }
-            gst_query_set_seeking(query, GST_FORMAT_BYTES, 1, 0, bytestream_len);
-            return TRUE;
-        }
-        case GST_QUERY_SCHEDULING:
-        {
-            gst_query_set_scheduling(query, GST_SCHEDULING_FLAG_SEEKABLE, 1, -1, 0);
-            gst_query_add_scheduling_mode(query, GST_PAD_MODE_PULL);
-            return TRUE;
-        }
-        default:
-        {
-            WARN("Unhandled query type %s\n", GST_QUERY_TYPE_NAME(query));
-            return FALSE;
-        }
-    }
-}
-
-static gboolean bytestream_pad_mode_activate(GstPad *pad, GstObject *parent, GstPadMode mode, gboolean activate)
-{
-    struct media_source *source = gst_pad_get_element_private(pad);
-
-    TRACE("%s source pad for mediasource %p in %s mode.\n",
-            activate ? "Activating" : "Deactivating", source, gst_pad_mode_get_name(mode));
-
-    return mode == GST_PAD_MODE_PULL;
-}
-
-static gboolean bytestream_pad_event_process(GstPad *pad, GstObject *parent, GstEvent *event)
-{
-    struct media_source *source = gst_pad_get_element_private(pad);
-
-    TRACE("source %p, type \"%s\".\n", source, GST_EVENT_TYPE_NAME(event));
-
-    switch (event->type) {
-        /* the seek event should fail in pull mode */
-        case GST_EVENT_SEEK:
-            return FALSE;
-        default:
-            WARN("Ignoring \"%s\" event.\n", GST_EVENT_TYPE_NAME(event));
-        case GST_EVENT_TAG:
-        case GST_EVENT_QOS:
-        case GST_EVENT_RECONFIGURE:
-            return gst_pad_event_default(pad, parent, event);
-    }
-    return TRUE;
-}
-
-GstBusSyncReply bus_watch(GstBus *bus, GstMessage *message, gpointer user)
-{
-    struct media_source *source = user;
-    gchar *dbg_info = NULL;
-    GError *err = NULL;
-
-    TRACE("source %p message type %s\n", source, 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;
-        default:
-            break;
-    }
-
-    gst_message_unref(message);
-    return GST_BUS_DROP;
-}
-
 static HRESULT WINAPI media_stream_QueryInterface(IMFMediaStream *iface, REFIID riid, void **out)
 {
     struct media_stream *stream = impl_from_IMFMediaStream(iface);
@@ -869,92 +717,28 @@ static const IMFMediaStreamVtbl media_stream_vtbl =
     media_stream_RequestSample
 };
 
-/* Setup a chain of elements which should hopefully allow transformations to any IMFMediaType
-   the user throws at us through gstreamer's caps negotiation. */
-static HRESULT media_stream_connect_to_sink(struct media_stream *stream)
-{
-    GstCaps *source_caps = gst_pad_query_caps(stream->their_src, NULL);
-    const gchar *stream_type;
-
-    if (!source_caps)
-        return E_FAIL;
-
-    stream_type = gst_structure_get_name(gst_caps_get_structure(source_caps, 0));
-    gst_caps_unref(source_caps);
-
-    if (!strcmp(stream_type, "video/x-raw"))
-    {
-        GstElement *videoconvert = gst_element_factory_make("videoconvert", NULL);
-
-        gst_bin_add(GST_BIN(stream->parent_source->container), videoconvert);
-
-        stream->my_sink = gst_element_get_static_pad(videoconvert, "sink");
-
-        if (!gst_element_link(videoconvert, stream->appsink))
-            return E_FAIL;
-
-        gst_element_sync_state_with_parent(videoconvert);
-    }
-    else if (!strcmp(stream_type, "audio/x-raw"))
-    {
-        GstElement *audioconvert = gst_element_factory_make("audioconvert", NULL);
-
-        gst_bin_add(GST_BIN(stream->parent_source->container), audioconvert);
-
-        stream->my_sink = gst_element_get_static_pad(audioconvert, "sink");
-
-        if (!gst_element_link(audioconvert, stream->appsink))
-            return E_FAIL;
-
-        gst_element_sync_state_with_parent(audioconvert);
-    }
-    else
-    {
-        stream->my_sink = gst_element_get_static_pad(stream->appsink, "sink");
-    }
-
-    if (gst_pad_link(stream->their_src, stream->my_sink) != GST_PAD_LINK_OK)
-        return E_FAIL;
-
-    return S_OK;
-}
-
-static HRESULT new_media_stream(struct media_source *source, GstPad *pad, DWORD stream_id, struct media_stream **out_stream)
+static HRESULT new_media_stream(struct media_source *source,
+        struct wg_parser_stream *wg_stream, DWORD stream_id, struct media_stream **out_stream)
 {
     struct media_stream *object = heap_alloc_zero(sizeof(*object));
     HRESULT hr;
 
-    TRACE("(%p %p)->(%p)\n", source, pad, out_stream);
+    TRACE("source %p, wg_stream %p, stream_id %u.\n", source, wg_stream, stream_id);
 
     object->IMFMediaStream_iface.lpVtbl = &media_stream_vtbl;
     object->ref = 1;
 
     IMFMediaSource_AddRef(&source->IMFMediaSource_iface);
     object->parent_source = source;
-    object->their_src = pad;
     object->stream_id = stream_id;
 
     object->state = STREAM_INACTIVE;
     object->eos = FALSE;
+    object->wg_stream = wg_stream;
 
     if (FAILED(hr = MFCreateEventQueue(&object->event_queue)))
         goto fail;
 
-    if (!(object->appsink = gst_element_factory_make("appsink", NULL)))
-    {
-        hr = E_OUTOFMEMORY;
-        goto fail;
-    }
-    gst_bin_add(GST_BIN(object->parent_source->container), object->appsink);
-
-    g_object_set(object->appsink, "sync", FALSE, NULL);
-    g_object_set(object->appsink, "max-buffers", 5, NULL);
-
-    if (FAILED(hr = media_stream_connect_to_sink(object)))
-        goto fail;
-
-    gst_element_sync_state_with_parent(object->appsink);
-
     TRACE("->(%p)\n", object);
     *out_stream = object;
 
@@ -969,18 +753,17 @@ fail:
 
 static HRESULT media_stream_init_desc(struct media_stream *stream)
 {
-    GstCaps *current_caps = gst_pad_get_current_caps(stream->their_src);
     IMFMediaTypeHandler *type_handler = NULL;
     IMFMediaType **stream_types = NULL;
     IMFMediaType *stream_type = NULL;
+    struct wg_format format;
     DWORD type_count = 0;
-    const gchar *major_type;
     unsigned int i;
     HRESULT hr;
 
-    major_type = gst_structure_get_name(gst_caps_get_structure(current_caps, 0));
+    unix_funcs->wg_parser_stream_get_preferred_format(stream->wg_stream, &format);
 
-    if (!strcmp(major_type, "video/x-raw"))
+    if (format.major_type == WG_MAJOR_TYPE_VIDEO)
     {
         /* These are the most common native output types of decoders:
             https://docs.microsoft.com/en-us/windows/win32/medfound/mft-decoder-expose-output-types-in-native-order */
@@ -993,7 +776,7 @@ static HRESULT media_stream_init_desc(struct media_stream *stream)
             &MFVideoFormat_I420,
         };
 
-        IMFMediaType *base_type = mf_media_type_from_caps(current_caps);
+        IMFMediaType *base_type = mf_media_type_from_wg_format(&format);
         GUID base_subtype;
 
         IMFMediaType_GetGUID(base_type, &MF_MT_SUBTYPE, &base_subtype);
@@ -1022,7 +805,7 @@ static HRESULT media_stream_init_desc(struct media_stream *stream)
     }
     else
     {
-        stream_type = mf_media_type_from_caps(current_caps);
+        stream_type = mf_media_type_from_wg_format(&format);
         if (stream_type)
         {
             stream_types = &stream_type;
@@ -1046,7 +829,6 @@ static HRESULT media_stream_init_desc(struct media_stream *stream)
         goto done;
 
 done:
-    gst_caps_unref(current_caps);
     if (type_handler)
         IMFMediaTypeHandler_Release(type_handler);
     for (i = 0; i < type_count; i++)
@@ -1237,16 +1019,7 @@ static HRESULT WINAPI media_source_Shutdown(IMFMediaSource *iface)
 
     source->state = SOURCE_SHUTDOWN;
 
-    if (source->container)
-    {
-        gst_element_set_state(source->container, GST_STATE_NULL);
-        gst_object_unref(GST_OBJECT(source->container));
-    }
-
-    if (source->my_src)
-        gst_object_unref(GST_OBJECT(source->my_src));
-    if (source->their_sink)
-        gst_object_unref(GST_OBJECT(source->their_sink));
+    unix_funcs->wg_parser_disconnect(source->wg_parser);
 
     if (source->pres_desc)
         IMFPresentationDescriptor_Release(source->pres_desc);
@@ -1261,8 +1034,6 @@ static HRESULT WINAPI media_source_Shutdown(IMFMediaSource *iface)
 
         stream->state = STREAM_SHUTDOWN;
 
-        if (stream->my_sink)
-            gst_object_unref(GST_OBJECT(stream->my_sink));
         if (stream->event_queue)
             IMFMediaEventQueue_Shutdown(stream->event_queue);
         if (stream->descriptor)
@@ -1276,24 +1047,15 @@ static HRESULT WINAPI media_source_Shutdown(IMFMediaSource *iface)
     if (source->read_thread)
     {
         source->read_thread_shutdown = true;
-        pthread_mutex_lock(&source->mutex);
-        source->shutdown = true;
-        pthread_mutex_unlock(&source->mutex);
-        pthread_cond_signal(&source->read_cond);
         WaitForSingleObject(source->read_thread, INFINITE);
         CloseHandle(source->read_thread);
     }
 
-    pthread_mutex_destroy(&source->mutex);
-    pthread_cond_destroy(&source->read_cond);
-    pthread_cond_destroy(&source->read_done_cond);
+    unix_funcs->wg_parser_destroy(source->wg_parser);
 
     if (source->stream_count)
         heap_free(source->streams);
 
-    if (source->no_more_pads_event)
-        CloseHandle(source->no_more_pads_event);
-
     if (source->async_commands_queue)
         MFUnlockWorkQueue(source->async_commands_queue);
 
@@ -1317,64 +1079,16 @@ static const IMFMediaSourceVtbl IMFMediaSource_vtbl =
     media_source_Shutdown,
 };
 
-static void stream_added(GstElement *element, GstPad *pad, gpointer user)
-{
-    struct media_source *source = user;
-    struct media_stream **new_stream_array;
-    struct media_stream *stream;
-
-    if (gst_pad_get_direction(pad) != GST_PAD_SRC)
-        return;
-
-    if (FAILED(new_media_stream(source, pad, source->stream_count, &stream)))
-        return;
-
-    if (!(new_stream_array = heap_realloc(source->streams, (source->stream_count + 1) * (sizeof(*new_stream_array)))))
-    {
-        ERR("Failed to add stream to source\n");
-        IMFMediaStream_Release(&stream->IMFMediaStream_iface);
-        return;
-    }
-
-    source->streams = new_stream_array;
-    source->streams[source->stream_count++] = stream;
-}
-
-static void stream_removed(GstElement *element, GstPad *pad, gpointer user)
-{
-    struct media_source *source = user;
-    unsigned int i;
-
-    for (i = 0; i < source->stream_count; i++)
-    {
-        struct media_stream *stream = source->streams[i];
-        if (stream->their_src != pad)
-            continue;
-        stream->their_src = NULL;
-        stream->state = STREAM_INACTIVE;
-    }
-}
-
-static void no_more_pads(GstElement *element, gpointer user)
-{
-    struct media_source *source = user;
-
-    SetEvent(source->no_more_pads_event);
-}
-
 static HRESULT media_source_constructor(IMFByteStream *bytestream, struct media_source **out_media_source)
 {
-    GstStaticPadTemplate src_template =
-        GST_STATIC_PAD_TEMPLATE("mf_src", GST_PAD_SRC, GST_PAD_ALWAYS, GST_STATIC_CAPS_ANY);
-
     IMFStreamDescriptor **descriptors = NULL;
     struct media_source *object;
     gint64 total_pres_time = 0;
+    struct wg_parser *parser;
     DWORD bytestream_caps;
     uint64_t file_size;
     unsigned int i;
     HRESULT hr;
-    int ret;
 
     if (FAILED(hr = IMFByteStream_GetCapabilities(bytestream, &bytestream_caps)))
         return hr;
@@ -1399,8 +1113,6 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, struct media_
     object->ref = 1;
     object->byte_stream = bytestream;
     IMFByteStream_AddRef(bytestream);
-    object->no_more_pads_event = CreateEventA(NULL, FALSE, FALSE, NULL);
-    object->file_size = file_size;
 
     if (FAILED(hr = MFCreateEventQueue(&object->event_queue)))
         goto fail;
@@ -1408,81 +1120,47 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, struct media_
     if (FAILED(hr = MFAllocateWorkQueue(&object->async_commands_queue)))
         goto fail;
 
-    pthread_mutex_init(&object->mutex, NULL);
-    pthread_cond_init(&object->read_cond, NULL);
-    pthread_cond_init(&object->read_done_cond, NULL);
-
     object->read_thread = CreateThread(NULL, 0, read_thread, object, 0, NULL);
 
-    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);
-    gst_element_set_bus(object->container, object->bus);
-
-    object->my_src = gst_pad_new_from_static_template(&src_template, "mf-src");
-    gst_pad_set_element_private(object->my_src, object);
-    gst_pad_set_getrange_function(object->my_src, bytestream_wrapper_pull_wrapper);
-    gst_pad_set_query_function(object->my_src, bytestream_query_wrapper);
-    gst_pad_set_activatemode_function(object->my_src, bytestream_pad_mode_activate_wrapper);
-    gst_pad_set_event_function(object->my_src, bytestream_pad_event_process_wrapper);
-
-    if (!(object->decodebin = gst_element_factory_make("decodebin", NULL)))
+    if (!(parser = unix_funcs->wg_decodebin_parser_create()))
     {
-        WARN("Failed to create decodebin for source\n");
         hr = E_OUTOFMEMORY;
         goto fail;
     }
+    object->wg_parser = parser;
 
-    /* In Media Foundation, sources may read from any media source stream
-       without fear of blocking due to buffering limits on another.  Trailmakers,
-       a Unity3D engine game does this by only reading from the audio stream once,
-       and never deselecting this.  These properties replicate that behavior.
-
-       Note that with most elements, this causes excessive memory use, however
-       this is also what occurs on Windows.
-    */
-    g_object_set(object->decodebin, "max-size-buffers", 0, NULL);
-    g_object_set(object->decodebin, "max-size-time", G_GUINT64_CONSTANT(0), NULL);
-    g_object_set(object->decodebin, "max-size-bytes", 0, NULL);
+    object->state = SOURCE_OPENING;
 
-    gst_bin_add(GST_BIN(object->container), object->decodebin);
+    if (FAILED(hr = unix_funcs->wg_parser_connect(parser, file_size)))
+        goto fail;
 
-    g_signal_connect(object->decodebin, "pad-added", G_CALLBACK(mf_src_stream_added_wrapper), object);
-    g_signal_connect(object->decodebin, "pad-removed", G_CALLBACK(mf_src_stream_removed_wrapper), object);
-    g_signal_connect(object->decodebin, "no-more-pads", G_CALLBACK(mf_src_no_more_pads_wrapper), object);
+    /* In Media Foundation, sources may read from any media source stream
+     * without fear of blocking due to buffering limits on another. Trailmakers,
+     * a Unity3D Engine game, only reads one sample from the audio stream (and
+     * never deselects it). Remove buffering limits from decodebin in order to
+     * account for this. Note that this does leak memory, but the same memory
+     * leak occurs with native. */
+    unix_funcs->wg_parser_set_unlimited_buffering(parser);
 
-    object->their_sink = gst_element_get_static_pad(object->decodebin, "sink");
+    object->stream_count = unix_funcs->wg_parser_get_stream_count(parser);
 
-    if ((ret = gst_pad_link(object->my_src, object->their_sink)) < 0)
+    if (!(object->streams = heap_alloc_zero(object->stream_count * sizeof(*object->streams))))
     {
-        WARN("Failed to link our bytestream pad to the demuxer input, error %d.\n", ret);
-        hr = E_FAIL;
+        hr = E_OUTOFMEMORY;
         goto fail;
     }
 
-    object->state = SOURCE_OPENING;
-
-    gst_element_set_state(object->container, GST_STATE_PAUSED);
-    ret = gst_element_get_state(object->container, NULL, NULL, -1);
-    if (ret == GST_STATE_CHANGE_FAILURE)
+    for (i = 0; i < object->stream_count; ++i)
     {
-        ERR("Failed to play source, error %d.\n", ret);
-        hr = E_FAIL;
-        goto fail;
-    }
+        if (FAILED(hr = new_media_stream(object, unix_funcs->wg_parser_get_stream(parser, i), i, &object->streams[i])))
+            goto fail;
 
-    WaitForSingleObject(object->no_more_pads_event, INFINITE);
-    for (i = 0; i < object->stream_count; i++)
-    {
-        GstSample *preroll;
-        g_signal_emit_by_name(object->streams[i]->appsink, "pull-preroll", &preroll);
         if (FAILED(hr = media_stream_init_desc(object->streams[i])))
         {
             ERR("Failed to finish initialization of media stream %p, hr %x.\n", object->streams[i], hr);
             IMFMediaStream_Release(&object->streams[i]->IMFMediaStream_iface);
             goto fail;
         }
-        gst_sample_unref(preroll);
     }
 
     /* init presentation descriptor */
@@ -1505,23 +1183,11 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, struct media_
     descriptors = NULL;
 
     for (i = 0; i < object->stream_count; i++)
-    {
-        gint64 stream_pres_time;
-        if (gst_pad_query_duration(object->streams[i]->their_src, GST_FORMAT_TIME, &stream_pres_time))
-        {
-            TRACE("Stream %u has duration %llu\n", i, (unsigned long long int) stream_pres_time);
-
-            if (stream_pres_time > total_pres_time)
-                total_pres_time = stream_pres_time;
-        }
-        else
-        {
-            WARN("Unable to get presentation time of stream %u\n", i);
-        }
-    }
+        total_pres_time = max(total_pres_time,
+                unix_funcs->wg_parser_stream_get_duration(object->streams[i]->wg_stream));
 
     if (object->stream_count)
-        IMFPresentationDescriptor_SetUINT64(object->pres_desc, &MF_PD_DURATION, total_pres_time / 100);
+        IMFPresentationDescriptor_SetUINT64(object->pres_desc, &MF_PD_DURATION, total_pres_time);
 
     object->state = SOURCE_STOPPED;
 
@@ -1992,64 +1658,3 @@ HRESULT winegstreamer_stream_handler_create(REFIID riid, void **obj)
 
     return hr;
 }
-
-/* helper for callback forwarding */
-void perform_cb_media_source(struct cb_data *cbdata)
-{
-    switch(cbdata->type)
-    {
-    case BYTESTREAM_WRAPPER_PULL:
-        {
-            struct getrange_data *data = &cbdata->u.getrange_data;
-            cbdata->u.getrange_data.ret = bytestream_wrapper_pull(data->pad, data->parent,
-                    data->ofs, data->len, data->buf);
-            break;
-        }
-    case BYTESTREAM_QUERY:
-        {
-            struct query_function_data *data = &cbdata->u.query_function_data;
-            cbdata->u.query_function_data.ret = bytestream_query(data->pad, data->parent, data->query);
-            break;
-        }
-    case BYTESTREAM_PAD_MODE_ACTIVATE:
-        {
-            struct activate_mode_data *data = &cbdata->u.activate_mode_data;
-            cbdata->u.activate_mode_data.ret = bytestream_pad_mode_activate(data->pad, data->parent, data->mode, data->activate);
-            break;
-        }
-    case BYTESTREAM_PAD_EVENT_PROCESS:
-        {
-            struct event_src_data *data = &cbdata->u.event_src_data;
-            cbdata->u.event_src_data.ret = bytestream_pad_event_process(data->pad, data->parent, data->event);
-            break;
-        }
-    case MF_SRC_BUS_WATCH:
-        {
-            struct watch_bus_data *data = &cbdata->u.watch_bus_data;
-            cbdata->u.watch_bus_data.ret = bus_watch(data->bus, data->msg, data->user);
-            break;
-        }
-    case MF_SRC_STREAM_ADDED:
-        {
-            struct pad_added_data *data = &cbdata->u.pad_added_data;
-            stream_added(data->element, data->pad, data->user);
-            break;
-        }
-    case MF_SRC_STREAM_REMOVED:
-        {
-            struct pad_removed_data *data = &cbdata->u.pad_removed_data;
-            stream_removed(data->element, data->pad, data->user);
-            break;
-        }
-    case MF_SRC_NO_MORE_PADS:
-        {
-            struct no_more_pads_data *data = &cbdata->u.no_more_pads_data;
-            no_more_pads(data->element, data->user);
-            break;
-        }
-    default:
-        {
-            assert(0);
-        }
-    }
-}
diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c
index f300988fc5c..8891facdffe 100644
--- a/dlls/winegstreamer/mfplat.c
+++ b/dlls/winegstreamer/mfplat.c
@@ -1,5 +1,6 @@
 /*
  * Copyright 2019 Nikolay Sivov for CodeWeavers
+ * Copyright 2020 Zebediah Figura for CodeWeavers
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -21,11 +22,14 @@
 
 #include "gst_private.h"
 
+#include <assert.h>
 #include <stdarg.h>
 
 #include "gst_private.h"
 #include "mfapi.h"
 #include "mfidl.h"
+#include "ks.h"
+#include "ksmedia.h"
 
 #include "wine/debug.h"
 #include "wine/heap.h"
@@ -517,351 +521,227 @@ HRESULT mfplat_DllRegisterServer(void)
 static const struct
 {
     const GUID *subtype;
-    GstVideoFormat format;
+    enum wg_video_format format;
+}
+video_formats[] =
+{
+    {&MFVideoFormat_ARGB32, WG_VIDEO_FORMAT_BGRA},
+    {&MFVideoFormat_RGB32,  WG_VIDEO_FORMAT_BGRx},
+    {&MFVideoFormat_RGB24,  WG_VIDEO_FORMAT_BGR},
+    {&MFVideoFormat_RGB555, WG_VIDEO_FORMAT_RGB15},
+    {&MFVideoFormat_RGB565, WG_VIDEO_FORMAT_RGB16},
+    {&MFVideoFormat_AYUV,   WG_VIDEO_FORMAT_AYUV},
+    {&MFVideoFormat_I420,   WG_VIDEO_FORMAT_I420},
+    {&MFVideoFormat_NV12,   WG_VIDEO_FORMAT_NV12},
+    {&MFVideoFormat_UYVY,   WG_VIDEO_FORMAT_UYVY},
+    {&MFVideoFormat_YUY2,   WG_VIDEO_FORMAT_YUY2},
+    {&MFVideoFormat_YV12,   WG_VIDEO_FORMAT_YV12},
+    {&MFVideoFormat_YVYU,   WG_VIDEO_FORMAT_YVYU},
+};
+
+static const struct
+{
+    const GUID *subtype;
+    UINT32 depth;
+    enum wg_audio_format format;
 }
-uncompressed_video_formats[] =
+audio_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},
+    {&MFAudioFormat_PCM,     8, WG_AUDIO_FORMAT_U8},
+    {&MFAudioFormat_PCM,    16, WG_AUDIO_FORMAT_S16LE},
+    {&MFAudioFormat_PCM,    24, WG_AUDIO_FORMAT_S24LE},
+    {&MFAudioFormat_PCM,    32, WG_AUDIO_FORMAT_S32LE},
+    {&MFAudioFormat_Float,  32, WG_AUDIO_FORMAT_F32LE},
+    {&MFAudioFormat_Float,  64, WG_AUDIO_FORMAT_F64LE},
 };
 
-/* returns NULL if doesn't match exactly */
-IMFMediaType *mf_media_type_from_caps(const GstCaps *caps)
+static inline UINT64 make_uint64(UINT32 high, UINT32 low)
 {
-    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;
+    return ((UINT64)high << 32) | low;
+}
 
-    info = gst_caps_get_structure(caps, 0);
-    mime_type = gst_structure_get_name(info);
+static IMFMediaType *mf_media_type_from_wg_format_audio(const struct wg_format *format)
+{
+    IMFMediaType *type;
+    unsigned int i;
 
-    if (!strncmp(mime_type, "video", 5))
+    for (i = 0; i < ARRAY_SIZE(audio_formats); ++i)
     {
-        GstVideoInfo video_info;
-
-        if (!gst_video_info_from_caps(&video_info, caps))
+        if (format->u.audio.format == audio_formats[i].format)
         {
-            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);
+            if (FAILED(MFCreateMediaType(&type)))
+                return NULL;
 
-        IMFMediaType_SetUINT64(media_type, &MF_MT_FRAME_RATE, ((UINT64)video_info.fps_n << 32) | video_info.fps_d);
+            IMFMediaType_SetGUID(type, &MF_MT_MAJOR_TYPE, &MFMediaType_Audio);
+            IMFMediaType_SetGUID(type, &MF_MT_SUBTYPE, audio_formats[i].subtype);
+            IMFMediaType_SetUINT32(type, &MF_MT_AUDIO_BITS_PER_SAMPLE, audio_formats[i].depth);
+            IMFMediaType_SetUINT32(type, &MF_MT_AUDIO_SAMPLES_PER_SECOND, format->u.audio.rate);
+            IMFMediaType_SetUINT32(type, &MF_MT_AUDIO_NUM_CHANNELS, format->u.audio.channels);
+            IMFMediaType_SetUINT32(type, &MF_MT_AUDIO_CHANNEL_MASK, format->u.audio.channel_mask);
 
-        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_video_formats); i++)
-                {
-                    if (uncompressed_video_formats[i].format == video_info.finfo->format)
-                    {
-                        IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, uncompressed_video_formats[i].subtype);
-                        break;
-                    }
-                }
-                if (i == ARRAY_SIZE(uncompressed_video_formats))
-                {
-                    FIXME("Unrecognized uncompressed video format %s\n", gst_video_format_to_string(video_info.finfo->format));
-                    IMFMediaType_Release(media_type);
-                    return NULL;
-                }
-            }
-        }
-        else
-        {
-            FIXME("Unrecognized video format %s\n", mime_type);
-            return NULL;
+            return type;
         }
     }
-    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, channel_mask);
+    return NULL;
+}
 
-        if (gst_structure_get_int(info, "bitrate", &bitrate))
-            IMFMediaType_SetUINT32(media_type, &MF_MT_AVG_BITRATE, bitrate);
+static IMFMediaType *mf_media_type_from_wg_format_video(const struct wg_format *format)
+{
+    IMFMediaType *type;
+    unsigned int i;
 
-        if (!strcmp(mime_type, "audio/x-raw"))
+    for (i = 0; i < ARRAY_SIZE(video_formats); ++i)
+    {
+        if (format->u.video.format == video_formats[i].format)
         {
-            GstAudioInfo audio_info;
-            DWORD depth;
-
-            if (!gst_audio_info_from_caps(&audio_info, caps))
-            {
-                ERR("Failed to get caps audio info\n");
-                IMFMediaType_Release(media_type);
+            if (FAILED(MFCreateMediaType(&type)))
                 return NULL;
-            }
 
-            depth = GST_AUDIO_INFO_DEPTH(&audio_info);
+            IMFMediaType_SetGUID(type, &MF_MT_MAJOR_TYPE, &MFMediaType_Video);
+            IMFMediaType_SetGUID(type, &MF_MT_SUBTYPE, video_formats[i].subtype);
+            IMFMediaType_SetUINT64(type, &MF_MT_FRAME_SIZE,
+                    make_uint64(format->u.video.width, format->u.video.height));
+            IMFMediaType_SetUINT64(type, &MF_MT_FRAME_RATE,
+                    make_uint64(format->u.video.fps_n, format->u.video.fps_d));
+            IMFMediaType_SetUINT32(type, &MF_MT_COMPRESSED, FALSE);
 
-            /* validation */
-            if ((audio_info.finfo->flags & GST_AUDIO_FORMAT_FLAG_INTEGER && depth > 8) ||
-                (audio_info.finfo->flags & GST_AUDIO_FORMAT_FLAG_SIGNED && depth <= 8) ||
-                (audio_info.finfo->endianness != G_LITTLE_ENDIAN && depth > 8))
-            {
-                IMFMediaType_Release(media_type);
-                return NULL;
-            }
-
-            /* conversion */
-            switch (audio_info.finfo->flags)
-            {
-                case GST_AUDIO_FORMAT_FLAG_FLOAT:
-                    IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &MFAudioFormat_Float);
-                    break;
-                case GST_AUDIO_FORMAT_FLAG_INTEGER:
-                case GST_AUDIO_FORMAT_FLAG_SIGNED:
-                    IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &MFAudioFormat_PCM);
-                    break;
-                default:
-                    FIXME("Unrecognized audio format %x\n", audio_info.finfo->format);
-                    IMFMediaType_Release(media_type);
-                    return NULL;
-            }
-
-            IMFMediaType_SetUINT32(media_type, &MF_MT_AUDIO_BITS_PER_SAMPLE, depth);
-        }
-        else
-        {
-            FIXME("Unrecognized audio format %s\n", mime_type);
-            IMFMediaType_Release(media_type);
-            return NULL;
+            return type;
         }
     }
-    else
-    {
-        IMFMediaType_Release(media_type);
-        return NULL;
-    }
 
-    return media_type;
+    return NULL;
 }
 
-GstCaps *caps_from_mf_media_type(IMFMediaType *type)
+IMFMediaType *mf_media_type_from_wg_format(const struct wg_format *format)
 {
-    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))
+    switch (format->major_type)
     {
-        UINT64 frame_rate = 0, frame_size = 0;
-        DWORD width, height;
-        GstVideoFormat format = GST_VIDEO_FORMAT_UNKNOWN;
-        GUID subtype_base;
-        GstVideoInfo info;
-        unsigned int i;
-
-        if (FAILED(IMFMediaType_GetUINT64(type, &MF_MT_FRAME_SIZE, &frame_size)))
+        case WG_MAJOR_TYPE_UNKNOWN:
             return NULL;
-        width = frame_size >> 32;
-        height = frame_size;
 
-        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;
-            }
-        }
+        case WG_MAJOR_TYPE_AUDIO:
+            return mf_media_type_from_wg_format_audio(format);
 
-        subtype_base = subtype;
-        subtype_base.Data1 = 0;
-        if (format == GST_VIDEO_FORMAT_UNKNOWN && IsEqualGUID(&MFVideoFormat_Base, &subtype_base))
-            format = gst_video_format_from_fourcc(subtype.Data1);
+        case WG_MAJOR_TYPE_VIDEO:
+            return mf_media_type_from_wg_format_video(format);
+    }
 
-        if (format == GST_VIDEO_FORMAT_UNKNOWN)
-        {
-            FIXME("Unrecognized format %s\n", debugstr_guid(&subtype));
-            return NULL;
-        }
+    assert(0);
+    return NULL;
+}
 
-        gst_video_info_set_format(&info, format, width, height);
-        output = gst_video_info_to_caps(&info);
+static void mf_media_type_to_wg_format_audio(IMFMediaType *type, struct wg_format *format)
+{
+    UINT32 rate, channels, channel_mask, depth;
+    unsigned int i;
+    GUID subtype;
 
-        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 (SUCCEEDED(IMFMediaType_GetUINT64(type, &MF_MT_FRAME_RATE, &frame_rate)))
-        {
-            /* Darksiders: Warmastered Edition uses a MF_MT_FRAME_RATE of 0,
-               and gstreamer won't accept an undefined number as the framerate. */
-            if (!(DWORD32)frame_rate)
-                frame_rate = 1;
-            gst_caps_set_simple(output, "framerate", GST_TYPE_FRACTION, (DWORD32)(frame_rate >> 32), (DWORD32) frame_rate, NULL);
-        }
-        return output;
+    if (FAILED(IMFMediaType_GetGUID(type, &MF_MT_SUBTYPE, &subtype)))
+    {
+        FIXME("Subtype is not set.\n");
+        return;
     }
-    else if (IsEqualGUID(&major_type, &MFMediaType_Audio))
+    if (FAILED(IMFMediaType_GetUINT32(type, &MF_MT_AUDIO_SAMPLES_PER_SECOND, &rate)))
     {
-        DWORD rate = -1, channels = -1, channel_mask = -1;
-
-        if (FAILED(IMFMediaType_GetUINT32(type, &MF_MT_AUDIO_SAMPLES_PER_SECOND, &rate)))
-        {
-            ERR("Sample rate not set.\n");
-            return NULL;
-        }
-        if (FAILED(IMFMediaType_GetUINT32(type, &MF_MT_AUDIO_NUM_CHANNELS, &channels)))
+        FIXME("Sample rate is not set.\n");
+        return;
+    }
+    if (FAILED(IMFMediaType_GetUINT32(type, &MF_MT_AUDIO_NUM_CHANNELS, &channels)))
+    {
+        FIXME("Channel count is not set.\n");
+        return;
+    }
+    if (FAILED(IMFMediaType_GetUINT32(type, &MF_MT_AUDIO_BITS_PER_SAMPLE, &depth)))
+    {
+        FIXME("Depth is not set.\n");
+        return;
+    }
+    if (FAILED(IMFMediaType_GetUINT32(type, &MF_MT_AUDIO_CHANNEL_MASK, &channel_mask)))
+    {
+        if (channels == 1)
+            channel_mask = KSAUDIO_SPEAKER_MONO;
+        else if (channels == 2)
+            channel_mask = KSAUDIO_SPEAKER_STEREO;
+        else
         {
-            ERR("Channel count not set.\n");
-            return NULL;
+            FIXME("Channel mask is not set.\n");
+            return;
         }
-        IMFMediaType_GetUINT32(type, &MF_MT_AUDIO_CHANNEL_MASK, &channel_mask);
+    }
 
-        if (IsEqualGUID(&subtype, &MFAudioFormat_Float))
-        {
-            GstAudioInfo float_info;
+    format->major_type = WG_MAJOR_TYPE_AUDIO;
+    format->u.audio.channels = channels;
+    format->u.audio.channel_mask = channel_mask;
+    format->u.audio.rate = rate;
 
-            gst_audio_info_set_format(&float_info, GST_AUDIO_FORMAT_F32LE, rate, channels, NULL);
-            output = gst_audio_info_to_caps(&float_info);
-        }
-        else if (IsEqualGUID(&subtype, &MFAudioFormat_PCM))
-        {
-            GstAudioFormat pcm_format;
-            GstAudioInfo pcm_info;
-            DWORD bits_per_sample;
-
-            if (SUCCEEDED(IMFMediaType_GetUINT32(type, &MF_MT_AUDIO_BITS_PER_SAMPLE, &bits_per_sample)))
-            {
-                pcm_format = gst_audio_format_build_integer(bits_per_sample > 8, G_LITTLE_ENDIAN, bits_per_sample, bits_per_sample);
-
-                gst_audio_info_set_format(&pcm_info, pcm_format, rate, channels, NULL);
-                output = gst_audio_info_to_caps(&pcm_info);
-            }
-            else
-            {
-                ERR("Bits per sample not set.\n");
-                return NULL;
-            }
-        }
-        else
+    for (i = 0; i < ARRAY_SIZE(audio_formats); ++i)
+    {
+        if (IsEqualGUID(&subtype, audio_formats[i].subtype) && depth == audio_formats[i].depth)
         {
-            FIXME("Unrecognized subtype %s\n", debugstr_guid(&subtype));
-            return NULL;
+            format->u.audio.format = audio_formats[i].format;
+            return;
         }
-
-        if (channel_mask != -1)
-            gst_caps_set_simple(output, "channel-mask", GST_TYPE_BITMASK, (guint64) channel_mask, NULL);
-
-        return output;
     }
-
-    FIXME("Unrecognized major type %s\n", debugstr_guid(&major_type));
-    return NULL;
+    FIXME("Unrecognized audio subtype %s, depth %u.\n", debugstr_guid(&subtype), depth);
 }
 
-/* IMFSample = GstBuffer
-   IMFBuffer = GstMemory */
-
-/* TODO: Future optimization could be to create a custom
-   IMFMediaBuffer wrapper around GstMemory, and to utilize
-   gst_memory_new_wrapped on IMFMediaBuffer data.  However,
-   this wouldn't work if we allow the callers to allocate
-   the buffers. */
-
-IMFSample* mf_sample_from_gst_buffer(GstBuffer *gst_buffer)
+static void mf_media_type_to_wg_format_video(IMFMediaType *type, struct wg_format *format)
 {
-    IMFMediaBuffer *mf_buffer = NULL;
-    GstMapInfo map_info = {0};
-    LONGLONG duration, time;
-    BYTE *mapped_buf = NULL;
-    IMFSample *out = NULL;
-    HRESULT hr;
-
-    if (FAILED(hr = MFCreateSample(&out)))
-        goto done;
-
-    duration = GST_BUFFER_DURATION(gst_buffer);
-    time = GST_BUFFER_PTS(gst_buffer);
-
-    if (FAILED(hr = IMFSample_SetSampleDuration(out, duration / 100)))
-        goto done;
-
-    if (FAILED(hr = IMFSample_SetSampleTime(out, time / 100)))
-        goto done;
+    UINT64 frame_rate, frame_size;
+    unsigned int i;
+    GUID subtype;
 
-    if (!gst_buffer_map(gst_buffer, &map_info, GST_MAP_READ))
+    if (FAILED(IMFMediaType_GetGUID(type, &MF_MT_SUBTYPE, &subtype)))
     {
-        hr = E_FAIL;
-        goto done;
+        FIXME("Subtype is not set.\n");
+        return;
+    }
+    if (FAILED(IMFMediaType_GetUINT64(type, &MF_MT_FRAME_SIZE, &frame_size)))
+    {
+        FIXME("Frame size is not set.\n");
+        return;
     }
 
-    if (FAILED(hr = MFCreateMemoryBuffer(map_info.maxsize, &mf_buffer)))
-        goto done;
-
-    if (FAILED(hr = IMFMediaBuffer_Lock(mf_buffer, &mapped_buf, NULL, NULL)))
-        goto done;
+    format->major_type = WG_MAJOR_TYPE_VIDEO;
+    format->u.video.width = (UINT32)(frame_size >> 32);
+    format->u.video.height = (UINT32)frame_size;
+    format->u.video.fps_n = 1;
+    format->u.video.fps_d = 1;
 
-    memcpy(mapped_buf, map_info.data, map_info.size);
+    if (SUCCEEDED(IMFMediaType_GetUINT64(type, &MF_MT_FRAME_RATE, &frame_rate)) && (UINT32)frame_rate)
+    {
+        format->u.video.fps_n = (UINT32)(frame_rate >> 32);
+        format->u.video.fps_d = (UINT32)frame_rate;
+    }
 
-    if (FAILED(hr = IMFMediaBuffer_Unlock(mf_buffer)))
-        goto done;
+    for (i = 0; i < ARRAY_SIZE(video_formats); ++i)
+    {
+        if (IsEqualGUID(&subtype, video_formats[i].subtype))
+        {
+            format->u.video.format = video_formats[i].format;
+            return;
+        }
+    }
+    FIXME("Unrecognized video subtype %s.\n", debugstr_guid(&subtype));
+}
 
-    if (FAILED(hr = IMFMediaBuffer_SetCurrentLength(mf_buffer, map_info.size)))
-        goto done;
+void mf_media_type_to_wg_format(IMFMediaType *type, struct wg_format *format)
+{
+    GUID major_type;
 
-    if (FAILED(hr = IMFSample_AddBuffer(out, mf_buffer)))
-        goto done;
+    memset(format, 0, sizeof(*format));
 
-done:
-    if (mf_buffer)
-        IMFMediaBuffer_Release(mf_buffer);
-    if (map_info.data)
-        gst_buffer_unmap(gst_buffer, &map_info);
-    if (FAILED(hr))
+    if (FAILED(IMFMediaType_GetMajorType(type, &major_type)))
     {
-        ERR("Failed to copy IMFSample to GstBuffer, hr = %#x\n", hr);
-        if (out)
-            IMFSample_Release(out);
-        out = NULL;
+        FIXME("Major type is not set.\n");
+        return;
     }
 
-    return out;
+    if (IsEqualGUID(&major_type, &MFMediaType_Audio))
+        mf_media_type_to_wg_format_audio(type, format);
+    else if (IsEqualGUID(&major_type, &MFMediaType_Video))
+        mf_media_type_to_wg_format_video(type, format);
+    else
+        FIXME("Unrecognized major type %s.\n", debugstr_guid(&major_type));
 }
diff --git a/dlls/winegstreamer/wg_parser.c b/dlls/winegstreamer/wg_parser.c
index 2540ecd80df..257da2538af 100644
--- a/dlls/winegstreamer/wg_parser.c
+++ b/dlls/winegstreamer/wg_parser.c
@@ -43,7 +43,7 @@ struct wg_parser
     struct wg_parser_stream **streams;
     unsigned int stream_count;
 
-    GstElement *container;
+    GstElement *container, *decodebin;
     GstBus *bus;
     GstPad *my_src, *their_sink;
 
@@ -540,6 +540,13 @@ static void CDECL wg_parser_complete_read_request(struct wg_parser *parser, bool
     pthread_cond_signal(&parser->read_done_cond);
 }
 
+static void CDECL wg_parser_set_unlimited_buffering(struct wg_parser *parser)
+{
+    g_object_set(parser->decodebin, "max-size-buffers", 0, NULL);
+    g_object_set(parser->decodebin, "max-size-time", G_GUINT64_CONSTANT(0), NULL);
+    g_object_set(parser->decodebin, "max-size-bytes", 0, NULL);
+}
+
 static void CDECL wg_parser_stream_get_preferred_format(struct wg_parser_stream *stream, struct wg_format *format)
 {
     *format = stream->preferred_format;
@@ -1602,6 +1609,7 @@ static BOOL decodebin_parser_init_gst(struct wg_parser *parser)
     }
 
     gst_bin_add(GST_BIN(parser->container), element);
+    parser->decodebin = element;
 
     g_signal_connect(element, "pad-added", G_CALLBACK(existing_new_pad), parser);
     g_signal_connect(element, "pad-removed", G_CALLBACK(removed_decoded_pad), parser);
@@ -1876,6 +1884,8 @@ static const struct unix_funcs funcs =
     wg_parser_get_read_request,
     wg_parser_complete_read_request,
 
+    wg_parser_set_unlimited_buffering,
+
     wg_parser_get_stream_count,
     wg_parser_get_stream,
 
-- 
2.30.1




More information about the wine-devel mailing list