[PATCH 2/4] winegstreamer: Move wg_parser object creation to a new Unix library.

Zebediah Figura z.figura12 at gmail.com
Thu Feb 11 16:18:56 CST 2021


Signed-off-by: Zebediah Figura <z.figura12 at gmail.com>
---
 dlls/winegstreamer/Makefile.in   |    3 +-
 dlls/winegstreamer/gst_cbs.h     |    6 -
 dlls/winegstreamer/gst_private.h |   88 +++
 dlls/winegstreamer/gstdemux.c    | 1029 +----------------------------
 dlls/winegstreamer/main.c        |    3 +
 dlls/winegstreamer/wg_parser.c   | 1030 ++++++++++++++++++++++++++++++
 6 files changed, 1129 insertions(+), 1030 deletions(-)
 create mode 100644 dlls/winegstreamer/wg_parser.c

diff --git a/dlls/winegstreamer/Makefile.in b/dlls/winegstreamer/Makefile.in
index 0b3229160b9..c5e4ed1ef42 100644
--- a/dlls/winegstreamer/Makefile.in
+++ b/dlls/winegstreamer/Makefile.in
@@ -16,7 +16,8 @@ C_SRCS = \
 	mfplat.c \
 	pin.c \
 	qualitycontrol.c \
-	seeking.c
+	seeking.c \
+	wg_parser.c
 
 IDL_SRCS = \
 	winegstreamer_classes.idl
diff --git a/dlls/winegstreamer/gst_cbs.h b/dlls/winegstreamer/gst_cbs.h
index fd90373d900..1daebee7906 100644
--- a/dlls/winegstreamer/gst_cbs.h
+++ b/dlls/winegstreamer/gst_cbs.h
@@ -23,12 +23,6 @@
 #include "windef.h"
 #include <pthread.h>
 
-typedef enum {
-  GST_AUTOPLUG_SELECT_TRY,
-  GST_AUTOPLUG_SELECT_EXPOSE,
-  GST_AUTOPLUG_SELECT_SKIP
-} GstAutoplugSelectResult;
-
 enum CB_TYPE {
     BYTESTREAM_WRAPPER_PULL,
     BYTESTREAM_QUERY,
diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h
index adef709d8fa..7aaf25e395e 100644
--- a/dlls/winegstreamer/gst_private.h
+++ b/dlls/winegstreamer/gst_private.h
@@ -43,6 +43,13 @@
 #include "wine/heap.h"
 #include "wine/strmbase.h"
 
+typedef enum
+{
+    GST_AUTOPLUG_SELECT_TRY,
+    GST_AUTOPLUG_SELECT_EXPOSE,
+    GST_AUTOPLUG_SELECT_SKIP,
+} GstAutoplugSelectResult;
+
 static inline const char *debugstr_time(REFERENCE_TIME time)
 {
     ULONGLONG abstime = time >= 0 ? time : -time;
@@ -126,6 +133,87 @@ struct wg_format
     } u;
 };
 
+struct wg_parser
+{
+    BOOL (*init_gst)(struct wg_parser *parser);
+
+    struct wg_parser_stream **streams;
+    unsigned int stream_count;
+
+    GstElement *container;
+    GstBus *bus;
+    GstPad *my_src, *their_sink;
+
+    guint64 file_size, start_offset, next_offset, stop_offset;
+
+    pthread_t push_thread;
+
+    pthread_mutex_t mutex;
+
+    pthread_cond_t init_cond;
+    bool no_more_pads, has_duration, error;
+
+    pthread_cond_t read_cond, read_done_cond;
+    struct
+    {
+        GstBuffer *buffer;
+        uint64_t offset;
+        uint32_t size;
+        bool done;
+        GstFlowReturn ret;
+    } read_request;
+
+    bool flushing, sink_connected;
+};
+
+enum wg_parser_event_type
+{
+    WG_PARSER_EVENT_NONE = 0,
+    WG_PARSER_EVENT_BUFFER,
+    WG_PARSER_EVENT_EOS,
+    WG_PARSER_EVENT_SEGMENT,
+};
+
+struct wg_parser_event
+{
+    enum wg_parser_event_type type;
+    union
+    {
+        GstBuffer *buffer;
+        struct
+        {
+            uint64_t position, stop;
+            double rate;
+        } segment;
+    } u;
+};
+
+struct wg_parser_stream
+{
+    struct wg_parser *parser;
+
+    GstPad *their_src, *post_sink, *post_src, *my_sink;
+    GstElement *flip;
+    struct wg_format preferred_format, current_format;
+
+    pthread_cond_t event_cond, event_empty_cond;
+    struct wg_parser_event event;
+
+    bool flushing, eos, enabled, has_caps;
+
+    uint64_t duration;
+};
+
+struct unix_funcs
+{
+    struct wg_parser *(CDECL *wg_decodebin_parser_create)(void);
+    struct wg_parser *(CDECL *wg_avi_parser_create)(void);
+    struct wg_parser *(CDECL *wg_mpeg_audio_parser_create)(void);
+    struct wg_parser *(CDECL *wg_wave_parser_create)(void);
+};
+
+extern const struct unix_funcs *unix_funcs;
+
 extern LONG object_locks;
 
 HRESULT avi_splitter_create(IUnknown *outer, IUnknown **out) DECLSPEC_HIDDEN;
diff --git a/dlls/winegstreamer/gstdemux.c b/dlls/winegstreamer/gstdemux.c
index 384ba9e6687..b4f95df67da 100644
--- a/dlls/winegstreamer/gstdemux.c
+++ b/dlls/winegstreamer/gstdemux.c
@@ -1,8 +1,9 @@
 /*
- * GStreamer splitter + decoder, adapted from parser.c
+ * DirectShow parser filters
  *
  * Copyright 2010 Maarten Lankhorst for CodeWeavers
  * Copyright 2010 Aric Stewart for CodeWeavers
+ * Copyright 2019-2020 Zebediah Figura
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -47,77 +48,6 @@ GST_DEBUG_CATEGORY_STATIC(wine);
 static const GUID MEDIASUBTYPE_CVID = {mmioFOURCC('c','v','i','d'), 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
 static const GUID MEDIASUBTYPE_MP3  = {WAVE_FORMAT_MPEGLAYER3, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
 
-struct wg_parser
-{
-    BOOL (*init_gst)(struct wg_parser *parser);
-
-    struct wg_parser_stream **streams;
-    unsigned int stream_count;
-
-    GstElement *container;
-    GstBus *bus;
-    GstPad *my_src, *their_sink;
-
-    guint64 file_size, start_offset, next_offset, stop_offset;
-
-    pthread_t push_thread;
-
-    pthread_mutex_t mutex;
-
-    pthread_cond_t init_cond;
-    bool no_more_pads, has_duration, error;
-
-    pthread_cond_t read_cond, read_done_cond;
-    struct
-    {
-        GstBuffer *buffer;
-        uint64_t offset;
-        uint32_t size;
-        bool done;
-        GstFlowReturn ret;
-    } read_request;
-
-    bool flushing, sink_connected;
-};
-
-enum wg_parser_event_type
-{
-    WG_PARSER_EVENT_NONE = 0,
-    WG_PARSER_EVENT_BUFFER,
-    WG_PARSER_EVENT_EOS,
-    WG_PARSER_EVENT_SEGMENT,
-};
-
-struct wg_parser_event
-{
-    enum wg_parser_event_type type;
-    union
-    {
-        GstBuffer *buffer;
-        struct
-        {
-            uint64_t position, stop;
-            double rate;
-        } segment;
-    } u;
-};
-
-struct wg_parser_stream
-{
-    struct wg_parser *parser;
-
-    GstPad *their_src, *post_sink, *post_src, *my_sink;
-    GstElement *flip;
-    struct wg_format preferred_format, current_format;
-
-    pthread_cond_t event_cond, event_empty_cond;
-    struct wg_parser_event event;
-
-    bool flushing, eos, enabled, has_caps;
-
-    uint64_t duration;
-};
-
 struct parser
 {
     struct strmbase_filter filter;
@@ -170,7 +100,6 @@ static const WCHAR wcsInputPinName[] = {'i','n','p','u','t',' ','p','i','n',0};
 static const IMediaSeekingVtbl GST_Seeking_Vtbl;
 static const IQualityControlVtbl GSTOutPin_QualityControl_Vtbl;
 
-static struct wg_parser_stream *create_stream(struct wg_parser *parser);
 static struct parser_source *create_pin(struct parser *filter,
         struct wg_parser_stream *stream, const WCHAR *name);
 static HRESULT GST_RemoveOutputPins(struct parser *This);
@@ -178,177 +107,6 @@ static HRESULT WINAPI GST_ChangeCurrent(IMediaSeeking *iface);
 static HRESULT WINAPI GST_ChangeStop(IMediaSeeking *iface);
 static HRESULT WINAPI GST_ChangeRate(IMediaSeeking *iface);
 
-static enum wg_audio_format wg_audio_format_from_gst(GstAudioFormat format)
-{
-    switch (format)
-    {
-        case GST_AUDIO_FORMAT_U8:
-            return WG_AUDIO_FORMAT_U8;
-        case GST_AUDIO_FORMAT_S16LE:
-            return WG_AUDIO_FORMAT_S16LE;
-        case GST_AUDIO_FORMAT_S24LE:
-            return WG_AUDIO_FORMAT_S24LE;
-        case GST_AUDIO_FORMAT_S32LE:
-            return WG_AUDIO_FORMAT_S32LE;
-        case GST_AUDIO_FORMAT_F32LE:
-            return WG_AUDIO_FORMAT_F32LE;
-        case GST_AUDIO_FORMAT_F64LE:
-            return WG_AUDIO_FORMAT_F64LE;
-        default:
-            return WG_AUDIO_FORMAT_UNKNOWN;
-    }
-}
-
-static void wg_format_from_audio_info(struct wg_format *format, const GstAudioInfo *info)
-{
-    format->major_type = WG_MAJOR_TYPE_AUDIO;
-    format->u.audio.format = wg_audio_format_from_gst(GST_AUDIO_INFO_FORMAT(info));
-    format->u.audio.channels = GST_AUDIO_INFO_CHANNELS(info);
-    format->u.audio.rate = GST_AUDIO_INFO_RATE(info);
-}
-
-static enum wg_video_format wg_video_format_from_gst(GstVideoFormat format)
-{
-    switch (format)
-    {
-        case GST_VIDEO_FORMAT_BGRA:
-            return WG_VIDEO_FORMAT_BGRA;
-        case GST_VIDEO_FORMAT_BGRx:
-            return WG_VIDEO_FORMAT_BGRx;
-        case GST_VIDEO_FORMAT_BGR:
-            return WG_VIDEO_FORMAT_BGR;
-        case GST_VIDEO_FORMAT_RGB15:
-            return WG_VIDEO_FORMAT_RGB15;
-        case GST_VIDEO_FORMAT_AYUV:
-            return WG_VIDEO_FORMAT_AYUV;
-        case GST_VIDEO_FORMAT_I420:
-            return WG_VIDEO_FORMAT_I420;
-        case GST_VIDEO_FORMAT_NV12:
-            return WG_VIDEO_FORMAT_NV12;
-        case GST_VIDEO_FORMAT_UYVY:
-            return WG_VIDEO_FORMAT_UYVY;
-        case GST_VIDEO_FORMAT_YUY2:
-            return WG_VIDEO_FORMAT_YUY2;
-        case GST_VIDEO_FORMAT_YV12:
-            return WG_VIDEO_FORMAT_YV12;
-        case GST_VIDEO_FORMAT_YVYU:
-            return WG_VIDEO_FORMAT_YVYU;
-        default:
-            return WG_VIDEO_FORMAT_UNKNOWN;
-    }
-}
-
-static void wg_format_from_video_info(struct wg_format *format, const GstVideoInfo *info)
-{
-    format->major_type = WG_MAJOR_TYPE_VIDEO;
-    format->u.video.format = wg_video_format_from_gst(GST_VIDEO_INFO_FORMAT(info));
-    format->u.video.width = GST_VIDEO_INFO_WIDTH(info);
-    format->u.video.height = GST_VIDEO_INFO_HEIGHT(info);
-    format->u.video.fps_n = GST_VIDEO_INFO_FPS_N(info);
-    format->u.video.fps_d = GST_VIDEO_INFO_FPS_D(info);
-}
-
-static void wg_format_from_caps_audio_mpeg(struct wg_format *format, const GstCaps *caps)
-{
-    const GstStructure *structure = gst_caps_get_structure(caps, 0);
-    gint layer, channels, rate;
-
-    if (!gst_structure_get_int(structure, "layer", &layer))
-    {
-        GST_WARNING("Missing \"layer\" value.");
-        return;
-    }
-    if (!gst_structure_get_int(structure, "channels", &channels))
-    {
-        GST_WARNING("Missing \"channels\" value.");
-        return;
-    }
-    if (!gst_structure_get_int(structure, "rate", &rate))
-    {
-        GST_WARNING("Missing \"rate\" value.");
-        return;
-    }
-
-    format->major_type = WG_MAJOR_TYPE_AUDIO;
-
-    if (layer == 1)
-        format->u.audio.format = WG_AUDIO_FORMAT_MPEG1_LAYER1;
-    else if (layer == 2)
-        format->u.audio.format = WG_AUDIO_FORMAT_MPEG1_LAYER2;
-    else if (layer == 3)
-        format->u.audio.format = WG_AUDIO_FORMAT_MPEG1_LAYER3;
-
-    format->u.audio.channels = channels;
-    format->u.audio.rate = rate;
-}
-
-static void wg_format_from_caps_video_cinepak(struct wg_format *format, const GstCaps *caps)
-{
-    const GstStructure *structure = gst_caps_get_structure(caps, 0);
-    gint width, height, fps_n, fps_d;
-
-    if (!gst_structure_get_int(structure, "width", &width))
-    {
-        GST_WARNING("Missing \"width\" value.");
-        return;
-    }
-    if (!gst_structure_get_int(structure, "height", &height))
-    {
-        GST_WARNING("Missing \"height\" value.");
-        return;
-    }
-    if (!gst_structure_get_fraction(structure, "framerate", &fps_n, &fps_d))
-    {
-        fps_n = 0;
-        fps_d = 1;
-    }
-
-    format->major_type = WG_MAJOR_TYPE_VIDEO;
-    format->u.video.format = WG_VIDEO_FORMAT_CINEPAK;
-    format->u.video.width = width;
-    format->u.video.height = height;
-    format->u.video.fps_n = fps_n;
-    format->u.video.fps_d = fps_d;
-}
-
-static void wg_format_from_caps(struct wg_format *format, const GstCaps *caps)
-{
-    const GstStructure *structure = gst_caps_get_structure(caps, 0);
-    const char *name = gst_structure_get_name(structure);
-
-    memset(format, 0, sizeof(*format));
-
-    if (!strcmp(name, "audio/x-raw"))
-    {
-        GstAudioInfo info;
-
-        if (gst_audio_info_from_caps(&info, caps))
-            wg_format_from_audio_info(format, &info);
-    }
-    else if (!strcmp(name, "video/x-raw"))
-    {
-        GstVideoInfo info;
-
-        if (gst_video_info_from_caps(&info, caps))
-            wg_format_from_video_info(format, &info);
-    }
-    else if (!strcmp(name, "audio/mpeg"))
-    {
-        wg_format_from_caps_audio_mpeg(format, caps);
-    }
-    else if (!strcmp(name, "video/x-cinepak"))
-    {
-        wg_format_from_caps_video_cinepak(format, caps);
-    }
-    else
-    {
-        gchar *str = gst_caps_to_string(caps);
-
-        GST_FIXME("Unhandled caps %s.", str);
-        g_free(str);
-    }
-}
-
 static DWORD channel_mask_from_count(uint32_t count)
 {
     switch (count)
@@ -811,177 +569,6 @@ static bool amt_to_wg_format(const AM_MEDIA_TYPE *mt, struct wg_format *format)
     return false;
 }
 
-static GstAudioFormat wg_audio_format_to_gst(enum wg_audio_format format)
-{
-    switch (format)
-    {
-        case WG_AUDIO_FORMAT_U8:    return GST_AUDIO_FORMAT_U8;
-        case WG_AUDIO_FORMAT_S16LE: return GST_AUDIO_FORMAT_S16LE;
-        case WG_AUDIO_FORMAT_S24LE: return GST_AUDIO_FORMAT_S24LE;
-        case WG_AUDIO_FORMAT_S32LE: return GST_AUDIO_FORMAT_S32LE;
-        case WG_AUDIO_FORMAT_F32LE: return GST_AUDIO_FORMAT_F32LE;
-        case WG_AUDIO_FORMAT_F64LE: return GST_AUDIO_FORMAT_F64LE;
-        default: return GST_AUDIO_FORMAT_UNKNOWN;
-    }
-}
-
-static GstCaps *wg_format_to_caps_audio(const struct wg_format *format)
-{
-    GstAudioFormat audio_format;
-    GstAudioInfo info;
-
-    if ((audio_format = wg_audio_format_to_gst(format->u.audio.format)) == GST_AUDIO_FORMAT_UNKNOWN)
-        return NULL;
-
-    gst_audio_info_set_format(&info, audio_format, format->u.audio.rate, format->u.audio.channels, NULL);
-    return gst_audio_info_to_caps(&info);
-}
-
-static GstVideoFormat wg_video_format_to_gst(enum wg_video_format format)
-{
-    switch (format)
-    {
-        case WG_VIDEO_FORMAT_BGRA:  return GST_VIDEO_FORMAT_BGRA;
-        case WG_VIDEO_FORMAT_BGRx:  return GST_VIDEO_FORMAT_BGRx;
-        case WG_VIDEO_FORMAT_BGR:   return GST_VIDEO_FORMAT_BGR;
-        case WG_VIDEO_FORMAT_RGB15: return GST_VIDEO_FORMAT_RGB15;
-        case WG_VIDEO_FORMAT_RGB16: return GST_VIDEO_FORMAT_RGB16;
-        case WG_VIDEO_FORMAT_AYUV:  return GST_VIDEO_FORMAT_AYUV;
-        case WG_VIDEO_FORMAT_I420:  return GST_VIDEO_FORMAT_I420;
-        case WG_VIDEO_FORMAT_NV12:  return GST_VIDEO_FORMAT_NV12;
-        case WG_VIDEO_FORMAT_UYVY:  return GST_VIDEO_FORMAT_UYVY;
-        case WG_VIDEO_FORMAT_YUY2:  return GST_VIDEO_FORMAT_YUY2;
-        case WG_VIDEO_FORMAT_YV12:  return GST_VIDEO_FORMAT_YV12;
-        case WG_VIDEO_FORMAT_YVYU:  return GST_VIDEO_FORMAT_YVYU;
-        default: return GST_VIDEO_FORMAT_UNKNOWN;
-    }
-}
-
-static GstCaps *wg_format_to_caps_video(const struct wg_format *format)
-{
-    GstVideoFormat video_format;
-    GstVideoInfo info;
-    unsigned int i;
-    GstCaps *caps;
-
-    if ((video_format = wg_video_format_to_gst(format->u.video.format)) == GST_VIDEO_FORMAT_UNKNOWN)
-        return NULL;
-
-    gst_video_info_set_format(&info, video_format, format->u.video.width, format->u.video.height);
-    if ((caps = gst_video_info_to_caps(&info)))
-    {
-        /* Clear some fields that shouldn't prevent us from connecting. */
-        for (i = 0; i < gst_caps_get_size(caps); ++i)
-        {
-            gst_structure_remove_fields(gst_caps_get_structure(caps, i),
-                    "framerate", "pixel-aspect-ratio", "colorimetry", "chroma-site", NULL);
-        }
-    }
-    return caps;
-}
-
-static GstCaps *wg_format_to_caps(const struct wg_format *format)
-{
-    switch (format->major_type)
-    {
-        case WG_MAJOR_TYPE_UNKNOWN:
-            return NULL;
-        case WG_MAJOR_TYPE_AUDIO:
-            return wg_format_to_caps_audio(format);
-        case WG_MAJOR_TYPE_VIDEO:
-            return wg_format_to_caps_video(format);
-    }
-    assert(0);
-    return NULL;
-}
-
-static bool wg_format_compare(const struct wg_format *a, const struct wg_format *b)
-{
-    if (a->major_type != b->major_type)
-        return false;
-
-    switch (a->major_type)
-    {
-        case WG_MAJOR_TYPE_UNKNOWN:
-            return false;
-
-        case WG_MAJOR_TYPE_AUDIO:
-            return a->u.audio.format == b->u.audio.format
-                    && a->u.audio.channels == b->u.audio.channels
-                    && a->u.audio.rate == b->u.audio.rate;
-
-        case WG_MAJOR_TYPE_VIDEO:
-            return a->u.video.format == b->u.video.format
-                    && a->u.video.width == b->u.video.width
-                    && a->u.video.height == b->u.video.height
-                    && a->u.video.fps_d * b->u.video.fps_n == a->u.video.fps_n * b->u.video.fps_d;
-    }
-
-    assert(0);
-    return false;
-}
-
-static gboolean query_sink(GstPad *pad, GstObject *parent, GstQuery *query)
-{
-    struct wg_parser_stream *stream = gst_pad_get_element_private(pad);
-
-    GST_LOG("stream %p, type \"%s\".", stream, gst_query_type_get_name(query->type));
-
-    switch (query->type)
-    {
-        case GST_QUERY_CAPS:
-        {
-            GstCaps *caps, *filter, *temp;
-
-            gst_query_parse_caps(query, &filter);
-
-            if (stream->enabled)
-                caps = wg_format_to_caps(&stream->current_format);
-            else
-                caps = gst_caps_new_any();
-            if (!caps)
-                return FALSE;
-
-            if (filter)
-            {
-                temp = gst_caps_intersect(caps, filter);
-                gst_caps_unref(caps);
-                caps = temp;
-            }
-
-            gst_query_set_caps_result(query, caps);
-            gst_caps_unref(caps);
-            return TRUE;
-        }
-        case GST_QUERY_ACCEPT_CAPS:
-        {
-            struct wg_format format;
-            gboolean ret = TRUE;
-            GstCaps *caps;
-
-            if (!stream->enabled)
-            {
-                gst_query_set_accept_caps_result(query, TRUE);
-                return TRUE;
-            }
-
-            gst_query_parse_accept_caps(query, &caps);
-            wg_format_from_caps(&format, caps);
-            ret = wg_format_compare(&format, &stream->current_format);
-            if (!ret && WARN_ON(gstreamer))
-            {
-                gchar *str = gst_caps_to_string(caps);
-                GST_WARNING("Rejecting caps \"%s\".", str);
-                g_free(str);
-            }
-            gst_query_set_accept_caps_result(query, ret);
-            return TRUE;
-        }
-        default:
-            return gst_pad_query_default (pad, parent, query);
-    }
-}
-
 static gboolean gst_base_src_perform_seek(struct wg_parser *parser, GstEvent *event)
 {
     gboolean res = TRUE;
@@ -1058,134 +645,6 @@ static gboolean event_src(GstPad *pad, GstObject *parent, GstEvent *event)
     return ret;
 }
 
-static GstFlowReturn queue_stream_event(struct wg_parser_stream *stream, const struct wg_parser_event *event)
-{
-    struct wg_parser *parser = stream->parser;
-
-    /* Unlike request_buffer_src() [q.v.], we need to watch for GStreamer
-     * flushes here. The difference is that we can be blocked by the streaming
-     * thread not running (or itself flushing on the DirectShow side).
-     * request_buffer_src() can only be blocked by the upstream source, and that
-     * is solved by flushing the upstream source. */
-
-    pthread_mutex_lock(&parser->mutex);
-    while (!stream->flushing && stream->event.type != WG_PARSER_EVENT_NONE)
-        pthread_cond_wait(&stream->event_empty_cond, &parser->mutex);
-    if (stream->flushing)
-    {
-        pthread_mutex_unlock(&parser->mutex);
-        GST_DEBUG("Filter is flushing; discarding event.");
-        return GST_FLOW_FLUSHING;
-    }
-    stream->event = *event;
-    pthread_mutex_unlock(&parser->mutex);
-    pthread_cond_signal(&stream->event_cond);
-    GST_LOG("Event queued.");
-    return GST_FLOW_OK;
-}
-
-static gboolean event_sink(GstPad *pad, GstObject *parent, GstEvent *event)
-{
-    struct wg_parser_stream *stream = gst_pad_get_element_private(pad);
-    struct wg_parser *parser = stream->parser;
-
-    GST_LOG("stream %p, type \"%s\".", stream, GST_EVENT_TYPE_NAME(event));
-
-    switch (event->type)
-    {
-        case GST_EVENT_SEGMENT:
-            if (stream->enabled)
-            {
-                struct wg_parser_event stream_event;
-                const GstSegment *segment;
-
-                gst_event_parse_segment(event, &segment);
-
-                if (segment->format != GST_FORMAT_TIME)
-                {
-                    GST_FIXME("Unhandled format \"%s\".", gst_format_get_name(segment->format));
-                    break;
-                }
-
-                stream_event.type = WG_PARSER_EVENT_SEGMENT;
-                stream_event.u.segment.position = segment->position / 100;
-                stream_event.u.segment.stop = segment->stop / 100;
-                stream_event.u.segment.rate = segment->rate * segment->applied_rate;
-                queue_stream_event(stream, &stream_event);
-            }
-            break;
-
-        case GST_EVENT_EOS:
-            if (stream->enabled)
-            {
-                struct wg_parser_event stream_event;
-
-                stream_event.type = WG_PARSER_EVENT_EOS;
-                queue_stream_event(stream, &stream_event);
-            }
-            else
-            {
-                pthread_mutex_lock(&parser->mutex);
-                stream->eos = true;
-                pthread_mutex_unlock(&parser->mutex);
-                pthread_cond_signal(&parser->init_cond);
-            }
-            break;
-
-        case GST_EVENT_FLUSH_START:
-            if (stream->enabled)
-            {
-                pthread_mutex_lock(&parser->mutex);
-
-                stream->flushing = true;
-                pthread_cond_signal(&stream->event_empty_cond);
-
-                switch (stream->event.type)
-                {
-                    case WG_PARSER_EVENT_NONE:
-                    case WG_PARSER_EVENT_EOS:
-                    case WG_PARSER_EVENT_SEGMENT:
-                        break;
-
-                    case WG_PARSER_EVENT_BUFFER:
-                        gst_buffer_unref(stream->event.u.buffer);
-                        break;
-                }
-                stream->event.type = WG_PARSER_EVENT_NONE;
-
-                pthread_mutex_unlock(&parser->mutex);
-            }
-            break;
-
-        case GST_EVENT_FLUSH_STOP:
-            if (stream->enabled)
-            {
-                pthread_mutex_lock(&parser->mutex);
-                stream->flushing = false;
-                pthread_mutex_unlock(&parser->mutex);
-            }
-            break;
-
-        case GST_EVENT_CAPS:
-        {
-            GstCaps *caps;
-
-            gst_event_parse_caps(event, &caps);
-            pthread_mutex_lock(&parser->mutex);
-            wg_format_from_caps(&stream->preferred_format, caps);
-            stream->has_caps = true;
-            pthread_mutex_unlock(&parser->mutex);
-            pthread_cond_signal(&parser->init_cond);
-            break;
-        }
-
-        default:
-            GST_WARNING("Ignoring \"%s\" event.", GST_EVENT_TYPE_NAME(event));
-    }
-    gst_event_unref(event);
-    return TRUE;
-}
-
 static GstFlowReturn request_buffer_src(GstPad *pad, GstObject *parent, guint64 offset, guint size, GstBuffer **buffer);
 
 static void *push_data(void *arg)
@@ -1237,28 +696,6 @@ static void *push_data(void *arg)
     return NULL;
 }
 
-static GstFlowReturn got_data_sink(GstPad *pad, GstObject *parent, GstBuffer *buffer)
-{
-    struct wg_parser_stream *stream = gst_pad_get_element_private(pad);
-    struct wg_parser_event stream_event;
-    GstFlowReturn ret;
-
-    GST_LOG("stream %p, buffer %p.", stream, buffer);
-
-    if (!stream->enabled)
-    {
-        gst_buffer_unref(buffer);
-        return GST_FLOW_OK;
-    }
-
-    stream_event.type = WG_PARSER_EVENT_BUFFER;
-    stream_event.u.buffer = buffer;
-    /* Transfer our reference to the buffer to the thread. */
-    if ((ret = queue_stream_event(stream, &stream_event)) != GST_FLOW_OK)
-        gst_buffer_unref(buffer);
-    return ret;
-}
-
 /* Fill and send a single IMediaSample. */
 static HRESULT send_sample(struct parser_source *pin, IMediaSample *sample,
         GstBuffer *buf, GstMapInfo *info, gsize offset, gsize size, DWORD bytes_per_second)
@@ -1552,180 +989,6 @@ static DWORD CALLBACK read_thread(void *arg)
     return 0;
 }
 
-static void removed_decoded_pad(GstElement *bin, GstPad *pad, gpointer user)
-{
-    struct wg_parser *parser = user;
-    unsigned int i;
-    char *name;
-
-    GST_LOG("parser %p, bin %p, pad %p.", parser, bin, pad);
-
-    for (i = 0; i < parser->stream_count; ++i)
-    {
-        struct wg_parser_stream *stream = parser->streams[i];
-
-        if (stream->their_src == pad)
-        {
-            if (stream->post_sink)
-                gst_pad_unlink(stream->their_src, stream->post_sink);
-            else
-                gst_pad_unlink(stream->their_src, stream->my_sink);
-            gst_object_unref(stream->their_src);
-            stream->their_src = NULL;
-            return;
-        }
-    }
-
-    name = gst_pad_get_name(pad);
-    GST_LOG("No pin matching pad \"%s\" found.", name);
-    g_free(name);
-}
-
-static void free_stream(struct wg_parser_stream *stream);
-
-static void init_new_decoded_pad(GstElement *bin, GstPad *pad, struct wg_parser *parser)
-{
-    struct wg_parser_stream *stream;
-    const char *typename;
-    GstCaps *caps;
-    GstStructure *arg;
-    int ret;
-
-    caps = gst_pad_query_caps(pad, NULL);
-    caps = gst_caps_make_writable(caps);
-    arg = gst_caps_get_structure(caps, 0);
-    typename = gst_structure_get_name(arg);
-
-    if (!(stream = create_stream(parser)))
-        goto out;
-
-    if (!strcmp(typename, "video/x-raw"))
-    {
-        GstElement *deinterlace, *vconv, *flip, *vconv2;
-
-        /* DirectShow can express interlaced video, but downstream filters can't
-         * necessarily consume it. In particular, the video renderer can't. */
-        if (!(deinterlace = gst_element_factory_make("deinterlace", NULL)))
-        {
-            fprintf(stderr, "winegstreamer: failed to create deinterlace, are %u-bit GStreamer \"good\" plugins installed?\n",
-                    8 * (int)sizeof(void *));
-            goto out;
-        }
-
-        /* decodebin considers many YUV formats to be "raw", but some quartz
-         * filters can't handle those. Also, videoflip can't handle all "raw"
-         * formats either. Add a videoconvert to swap color spaces. */
-        if (!(vconv = gst_element_factory_make("videoconvert", NULL)))
-        {
-            fprintf(stderr, "winegstreamer: failed to create videoconvert, are %u-bit GStreamer \"base\" plugins installed?\n",
-                    8 * (int)sizeof(void *));
-            goto out;
-        }
-
-        /* GStreamer outputs RGB video top-down, but DirectShow expects bottom-up. */
-        if (!(flip = gst_element_factory_make("videoflip", NULL)))
-        {
-            fprintf(stderr, "winegstreamer: failed to create videoflip, are %u-bit GStreamer \"good\" plugins installed?\n",
-                    8 * (int)sizeof(void *));
-            goto out;
-        }
-
-        /* videoflip does not support 15 and 16-bit RGB so add a second videoconvert
-         * to do the final conversion. */
-        if (!(vconv2 = gst_element_factory_make("videoconvert", NULL)))
-        {
-            fprintf(stderr, "winegstreamer: failed to create videoconvert, are %u-bit GStreamer \"base\" plugins installed?\n",
-                    8 * (int)sizeof(void *));
-            goto out;
-        }
-
-        /* The bin takes ownership of these elements. */
-        gst_bin_add(GST_BIN(parser->container), deinterlace);
-        gst_element_sync_state_with_parent(deinterlace);
-        gst_bin_add(GST_BIN(parser->container), vconv);
-        gst_element_sync_state_with_parent(vconv);
-        gst_bin_add(GST_BIN(parser->container), flip);
-        gst_element_sync_state_with_parent(flip);
-        gst_bin_add(GST_BIN(parser->container), vconv2);
-        gst_element_sync_state_with_parent(vconv2);
-
-        gst_element_link(deinterlace, vconv);
-        gst_element_link(vconv, flip);
-        gst_element_link(flip, vconv2);
-
-        stream->post_sink = gst_element_get_static_pad(deinterlace, "sink");
-        stream->post_src = gst_element_get_static_pad(vconv2, "src");
-        stream->flip = flip;
-    }
-    else if (!strcmp(typename, "audio/x-raw"))
-    {
-        GstElement *convert;
-
-        /* Currently our dsound can't handle 64-bit formats or all
-         * surround-sound configurations. Native dsound can't always handle
-         * 64-bit formats either. Add an audioconvert to allow changing bit
-         * depth and channel count. */
-        if (!(convert = gst_element_factory_make("audioconvert", NULL)))
-        {
-            fprintf(stderr, "winegstreamer: failed to create audioconvert, are %u-bit GStreamer \"base\" plugins installed?\n",
-                    8 * (int)sizeof(void *));
-            goto out;
-        }
-
-        gst_bin_add(GST_BIN(parser->container), convert);
-        gst_element_sync_state_with_parent(convert);
-
-        stream->post_sink = gst_element_get_static_pad(convert, "sink");
-        stream->post_src = gst_element_get_static_pad(convert, "src");
-    }
-
-    if (stream->post_sink)
-    {
-        if ((ret = gst_pad_link(pad, stream->post_sink)) < 0)
-        {
-            GST_ERROR("Failed to link decodebin source pad to post-processing elements, error %s.",
-                    gst_pad_link_get_name(ret));
-            gst_object_unref(stream->post_sink);
-            stream->post_sink = NULL;
-            goto out;
-        }
-
-        if ((ret = gst_pad_link(stream->post_src, stream->my_sink)) < 0)
-        {
-            GST_ERROR("Failed to link post-processing elements to our sink pad, error %s.",
-                    gst_pad_link_get_name(ret));
-            gst_object_unref(stream->post_src);
-            stream->post_src = NULL;
-            gst_object_unref(stream->post_sink);
-            stream->post_sink = NULL;
-            goto out;
-        }
-    }
-    else if ((ret = gst_pad_link(pad, stream->my_sink)) < 0)
-    {
-        GST_ERROR("Failed to link decodebin source pad to our sink pad, error %s.",
-                gst_pad_link_get_name(ret));
-        goto out;
-    }
-
-    gst_pad_set_active(stream->my_sink, 1);
-    gst_object_ref(stream->their_src = pad);
-out:
-    gst_caps_unref(caps);
-}
-
-static void existing_new_pad(GstElement *bin, GstPad *pad, gpointer user)
-{
-    struct wg_parser *This = user;
-
-    GST_LOG("parser %p, bin %p, pad %p.", This, bin, pad);
-
-    if (gst_pad_is_linked(pad))
-        return;
-
-    init_new_decoded_pad(bin, pad, This);
-}
-
 static gboolean query_function(GstPad *pad, GstObject *parent, GstQuery *query)
 {
     struct wg_parser *parser = gst_pad_get_element_private(pad);
@@ -1807,37 +1070,6 @@ static gboolean activate_mode(GstPad *pad, GstObject *parent, GstPadMode mode, g
     return FALSE;
 }
 
-static void no_more_pads(GstElement *decodebin, gpointer user)
-{
-    struct wg_parser *parser = user;
-
-    GST_DEBUG("parser %p.", parser);
-
-    pthread_mutex_lock(&parser->mutex);
-    parser->no_more_pads = true;
-    pthread_mutex_unlock(&parser->mutex);
-    pthread_cond_signal(&parser->init_cond);
-}
-
-static GstAutoplugSelectResult autoplug_blacklist(GstElement *bin, GstPad *pad, GstCaps *caps, GstElementFactory *fact, gpointer user)
-{
-    const char *name = gst_element_factory_get_longname(fact);
-
-    GST_TRACE("Using \"%s\".", name);
-
-    if (strstr(name, "Player protection"))
-    {
-        GST_WARNING("Blacklisted a/52 decoder because it only works in Totem.");
-        return GST_AUTOPLUG_SELECT_SKIP;
-    }
-    if (!strcmp(name, "Fluendo Hardware Accelerated Video Decoder"))
-    {
-        GST_WARNING("Disabled video acceleration since it breaks in wine.");
-        return GST_AUTOPLUG_SELECT_SKIP;
-    }
-    return GST_AUTOPLUG_SELECT_TRY;
-}
-
 static GstBusSyncReply watch_bus(GstBus *bus, GstMessage *msg, gpointer data)
 {
     struct parser *filter = data;
@@ -2191,58 +1423,6 @@ static const struct strmbase_sink_ops sink_ops =
     .sink_disconnect = parser_sink_disconnect,
 };
 
-static BOOL decodebin_parser_init_gst(struct wg_parser *parser)
-{
-    GstElement *element = gst_element_factory_make("decodebin", NULL);
-    int ret;
-
-    if (!element)
-    {
-        ERR("Failed to create decodebin; are %u-bit GStreamer \"base\" plugins installed?\n",
-                8 * (int)sizeof(void*));
-        return FALSE;
-    }
-
-    gst_bin_add(GST_BIN(parser->container), 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);
-    g_signal_connect(element, "autoplug-select", G_CALLBACK(autoplug_blacklist), parser);
-    g_signal_connect(element, "no-more-pads", G_CALLBACK(no_more_pads), parser);
-
-    parser->their_sink = gst_element_get_static_pad(element, "sink");
-
-    pthread_mutex_lock(&parser->mutex);
-    parser->no_more_pads = parser->error = false;
-    pthread_mutex_unlock(&parser->mutex);
-
-    if ((ret = gst_pad_link(parser->my_src, parser->their_sink)) < 0)
-    {
-        ERR("Failed to link pads, error %d.\n", ret);
-        return FALSE;
-    }
-
-    gst_element_set_state(parser->container, GST_STATE_PAUSED);
-    ret = gst_element_get_state(parser->container, NULL, NULL, -1);
-    if (ret == GST_STATE_CHANGE_FAILURE)
-    {
-        ERR("Failed to play stream.\n");
-        return FALSE;
-    }
-
-    pthread_mutex_lock(&parser->mutex);
-    while (!parser->no_more_pads && !parser->error)
-        pthread_cond_wait(&parser->init_cond, &parser->mutex);
-    if (parser->error)
-    {
-        pthread_mutex_unlock(&parser->mutex);
-        return FALSE;
-    }
-    pthread_mutex_unlock(&parser->mutex);
-
-    return TRUE;
-}
-
 static BOOL decodebin_parser_filter_init_gst(struct parser *filter)
 {
     static const WCHAR formatW[] = {'S','t','r','e','a','m',' ','%','0','2','u',0};
@@ -2328,23 +1508,6 @@ static BOOL parser_init_gstreamer(void)
     return TRUE;
 }
 
-static struct wg_parser *wg_parser_create(void)
-{
-    struct wg_parser *parser;
-
-    if (!(parser = calloc(1, sizeof(*parser))))
-        return NULL;
-
-    pthread_mutex_init(&parser->mutex, NULL);
-    pthread_cond_init(&parser->init_cond, NULL);
-    pthread_cond_init(&parser->read_cond, NULL);
-    pthread_cond_init(&parser->read_done_cond, NULL);
-    parser->flushing = true;
-
-    TRACE("Created winegstreamer parser %p.\n", parser);
-    return parser;
-}
-
 HRESULT decodebin_parser_create(IUnknown *outer, IUnknown **out)
 {
     struct parser *object;
@@ -2357,12 +1520,11 @@ HRESULT decodebin_parser_create(IUnknown *outer, IUnknown **out)
     if (!(object = heap_alloc_zero(sizeof(*object))))
         return E_OUTOFMEMORY;
 
-    if (!(object->wg_parser = wg_parser_create()))
+    if (!(object->wg_parser = unix_funcs->wg_decodebin_parser_create()))
     {
         heap_free(object);
         return E_OUTOFMEMORY;
     }
-    object->wg_parser->init_gst = decodebin_parser_init_gst;
 
     strmbase_filter_init(&object->filter, outer, &CLSID_decodebin_parser, &filter_ops);
     strmbase_sink_init(&object->sink, &object->filter, wcsInputPinName, &sink_ops, NULL);
@@ -2827,33 +1989,6 @@ static const struct strmbase_source_ops source_ops =
     .source_disconnect = source_disconnect,
 };
 
-static struct wg_parser_stream *create_stream(struct wg_parser *parser)
-{
-    struct wg_parser_stream *stream, **new_array;
-    char pad_name[19];
-
-    if (!(new_array = realloc(parser->streams, (parser->stream_count + 1) * sizeof(*parser->streams))))
-        return NULL;
-    parser->streams = new_array;
-
-    if (!(stream = calloc(1, sizeof(*stream))))
-        return NULL;
-
-    stream->parser = parser;
-    pthread_cond_init(&stream->event_cond, NULL);
-    pthread_cond_init(&stream->event_empty_cond, NULL);
-
-    sprintf(pad_name, "qz_sink_%u", parser->stream_count);
-    stream->my_sink = gst_pad_new(pad_name, GST_PAD_SINK);
-    gst_pad_set_element_private(stream->my_sink, stream);
-    gst_pad_set_chain_function(stream->my_sink, got_data_sink);
-    gst_pad_set_event_function(stream->my_sink, event_sink);
-    gst_pad_set_query_function(stream->my_sink, query_sink);
-
-    parser->streams[parser->stream_count++] = stream;
-    return stream;
-}
-
 static struct parser_source *create_pin(struct parser *filter,
         struct wg_parser_stream *stream, const WCHAR *name)
 {
@@ -2962,51 +2097,6 @@ static const struct strmbase_sink_ops wave_parser_sink_ops =
     .sink_disconnect = parser_sink_disconnect,
 };
 
-static BOOL wave_parser_init_gst(struct wg_parser *parser)
-{
-    struct wg_parser_stream *stream;
-    GstElement *element;
-    int ret;
-
-    if (!(element = gst_element_factory_make("wavparse", NULL)))
-    {
-        ERR("Failed to create wavparse; are %u-bit GStreamer \"good\" plugins installed?\n",
-                8 * (int)sizeof(void*));
-        return FALSE;
-    }
-
-    gst_bin_add(GST_BIN(parser->container), element);
-
-    parser->their_sink = gst_element_get_static_pad(element, "sink");
-    if ((ret = gst_pad_link(parser->my_src, parser->their_sink)) < 0)
-    {
-        ERR("Failed to link sink pads, error %d.\n", ret);
-        return FALSE;
-    }
-
-    if (!(stream = create_stream(parser)))
-        return FALSE;
-
-    stream->their_src = gst_element_get_static_pad(element, "src");
-    gst_object_ref(stream->their_src);
-    if ((ret = gst_pad_link(stream->their_src, stream->my_sink)) < 0)
-    {
-        ERR("Failed to link source pads, error %d.\n", ret);
-        return FALSE;
-    }
-
-    gst_pad_set_active(stream->my_sink, 1);
-    gst_element_set_state(parser->container, GST_STATE_PAUSED);
-    ret = gst_element_get_state(parser->container, NULL, NULL, -1);
-    if (ret == GST_STATE_CHANGE_FAILURE)
-    {
-        ERR("Failed to play stream.\n");
-        return FALSE;
-    }
-
-    return TRUE;
-}
-
 static BOOL wave_parser_filter_init_gst(struct parser *filter)
 {
     static const WCHAR source_name[] = {'o','u','t','p','u','t',0};
@@ -3056,12 +2146,11 @@ HRESULT wave_parser_create(IUnknown *outer, IUnknown **out)
     if (!(object = heap_alloc_zero(sizeof(*object))))
         return E_OUTOFMEMORY;
 
-    if (!(object->wg_parser = wg_parser_create()))
+    if (!(object->wg_parser = unix_funcs->wg_wave_parser_create()))
     {
         heap_free(object);
         return E_OUTOFMEMORY;
     }
-    object->wg_parser->init_gst = wave_parser_init_gst;
 
     strmbase_filter_init(&object->filter, outer, &CLSID_WAVEParser, &filter_ops);
     strmbase_sink_init(&object->sink, &object->filter, sink_name, &wave_parser_sink_ops, NULL);
@@ -3089,57 +2178,6 @@ static const struct strmbase_sink_ops avi_splitter_sink_ops =
     .sink_disconnect = parser_sink_disconnect,
 };
 
-static BOOL avi_parser_init_gst(struct wg_parser *parser)
-{
-    GstElement *element = gst_element_factory_make("avidemux", NULL);
-    int ret;
-
-    if (!element)
-    {
-        ERR("Failed to create avidemux; are %u-bit GStreamer \"good\" plugins installed?\n",
-                8 * (int)sizeof(void*));
-        return FALSE;
-    }
-
-    gst_bin_add(GST_BIN(parser->container), 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);
-    g_signal_connect(element, "no-more-pads", G_CALLBACK(no_more_pads), parser);
-
-    parser->their_sink = gst_element_get_static_pad(element, "sink");
-
-    pthread_mutex_lock(&parser->mutex);
-    parser->no_more_pads = parser->error = false;
-    pthread_mutex_unlock(&parser->mutex);
-
-    if ((ret = gst_pad_link(parser->my_src, parser->their_sink)) < 0)
-    {
-        ERR("Failed to link pads, error %d.\n", ret);
-        return FALSE;
-    }
-
-    gst_element_set_state(parser->container, GST_STATE_PAUSED);
-    ret = gst_element_get_state(parser->container, NULL, NULL, -1);
-    if (ret == GST_STATE_CHANGE_FAILURE)
-    {
-        ERR("Failed to play stream.\n");
-        return FALSE;
-    }
-
-    pthread_mutex_lock(&parser->mutex);
-    while (!parser->no_more_pads && !parser->error)
-        pthread_cond_wait(&parser->init_cond, &parser->mutex);
-    if (parser->error)
-    {
-        pthread_mutex_unlock(&parser->mutex);
-        return FALSE;
-    }
-    pthread_mutex_unlock(&parser->mutex);
-
-    return TRUE;
-}
-
 static BOOL avi_splitter_filter_init_gst(struct parser *filter)
 {
     static const WCHAR formatW[] = {'S','t','r','e','a','m',' ','%','0','2','u',0};
@@ -3195,12 +2233,11 @@ HRESULT avi_splitter_create(IUnknown *outer, IUnknown **out)
     if (!(object = heap_alloc_zero(sizeof(*object))))
         return E_OUTOFMEMORY;
 
-    if (!(object->wg_parser = wg_parser_create()))
+    if (!(object->wg_parser = unix_funcs->wg_avi_parser_create()))
     {
         heap_free(object);
         return E_OUTOFMEMORY;
     }
-    object->wg_parser->init_gst = avi_parser_init_gst;
 
     strmbase_filter_init(&object->filter, outer, &CLSID_AviSplitter, &filter_ops);
     strmbase_sink_init(&object->sink, &object->filter, sink_name, &avi_splitter_sink_ops, NULL);
@@ -3233,59 +2270,6 @@ static const struct strmbase_sink_ops mpeg_splitter_sink_ops =
     .sink_disconnect = parser_sink_disconnect,
 };
 
-static BOOL mpeg_audio_parser_init_gst(struct wg_parser *parser)
-{
-    struct wg_parser_stream *stream;
-    GstElement *element;
-    int ret;
-
-    if (!(element = gst_element_factory_make("mpegaudioparse", NULL)))
-    {
-        ERR("Failed to create mpegaudioparse; are %u-bit GStreamer \"good\" plugins installed?\n",
-                8 * (int)sizeof(void*));
-        return FALSE;
-    }
-
-    gst_bin_add(GST_BIN(parser->container), element);
-
-    parser->their_sink = gst_element_get_static_pad(element, "sink");
-    if ((ret = gst_pad_link(parser->my_src, parser->their_sink)) < 0)
-    {
-        ERR("Failed to link sink pads, error %d.\n", ret);
-        return FALSE;
-    }
-
-    if (!(stream = create_stream(parser)))
-        return FALSE;
-
-    gst_object_ref(stream->their_src = gst_element_get_static_pad(element, "src"));
-    if ((ret = gst_pad_link(stream->their_src, stream->my_sink)) < 0)
-    {
-        ERR("Failed to link source pads, error %d.\n", ret);
-        return FALSE;
-    }
-
-    gst_pad_set_active(stream->my_sink, 1);
-    gst_element_set_state(parser->container, GST_STATE_PAUSED);
-    ret = gst_element_get_state(parser->container, NULL, NULL, -1);
-    if (ret == GST_STATE_CHANGE_FAILURE)
-    {
-        ERR("Failed to play stream.\n");
-        return FALSE;
-    }
-
-    pthread_mutex_lock(&parser->mutex);
-    while (!parser->has_duration && !parser->error && !stream->eos)
-        pthread_cond_wait(&parser->init_cond, &parser->mutex);
-    if (parser->error)
-    {
-        pthread_mutex_unlock(&parser->mutex);
-        return FALSE;
-    }
-    pthread_mutex_unlock(&parser->mutex);
-    return TRUE;
-}
-
 static BOOL mpeg_splitter_filter_init_gst(struct parser *filter)
 {
     static const WCHAR source_name[] = {'A','u','d','i','o',0};
@@ -3358,12 +2342,11 @@ HRESULT mpeg_splitter_create(IUnknown *outer, IUnknown **out)
     if (!(object = heap_alloc_zero(sizeof(*object))))
         return E_OUTOFMEMORY;
 
-    if (!(object->wg_parser = wg_parser_create()))
+    if (!(object->wg_parser = unix_funcs->wg_mpeg_audio_parser_create()))
     {
         heap_free(object);
         return E_OUTOFMEMORY;
     }
-    object->wg_parser->init_gst = mpeg_audio_parser_init_gst;
 
     strmbase_filter_init(&object->filter, outer, &CLSID_MPEG1Splitter, &mpeg_splitter_ops);
     strmbase_sink_init(&object->sink, &object->filter, sink_name, &mpeg_splitter_sink_ops, NULL);
diff --git a/dlls/winegstreamer/main.c b/dlls/winegstreamer/main.c
index 3c630a1561f..477ed0ad1e6 100644
--- a/dlls/winegstreamer/main.c
+++ b/dlls/winegstreamer/main.c
@@ -40,12 +40,15 @@ static const WCHAR avi_splitterW[] =
 static const WCHAR mpeg_splitterW[] =
 {'M','P','E','G','-','I',' ','S','t','r','e','a','m',' ','S','p','l','i','t','t','e','r',0};
 
+const struct unix_funcs *unix_funcs = NULL;
+
 BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, void *reserved)
 {
     if (reason == DLL_PROCESS_ATTACH)
     {
         winegstreamer_instance = instance;
         DisableThreadLibraryCalls(instance);
+        __wine_init_unix_lib(instance, reason, NULL, &unix_funcs);
     }
     return TRUE;
 }
diff --git a/dlls/winegstreamer/wg_parser.c b/dlls/winegstreamer/wg_parser.c
new file mode 100644
index 00000000000..a64c3f83497
--- /dev/null
+++ b/dlls/winegstreamer/wg_parser.c
@@ -0,0 +1,1030 @@
+/*
+ * GStreamer parser backend
+ *
+ * Copyright 2010 Maarten Lankhorst for CodeWeavers
+ * Copyright 2010 Aric Stewart for CodeWeavers
+ * Copyright 2019-2020 Zebediah Figura
+ *
+ * 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
+ */
+
+#if 0
+#pragma makedep unix
+#endif
+
+#include "config.h"
+#include "ntstatus.h"
+#define WIN32_NO_STATUS
+#include "gst_private.h"
+#include "winternl.h"
+#include <assert.h>
+
+WINE_DEFAULT_DEBUG_CHANNEL(gstreamer);
+
+GST_DEBUG_CATEGORY_STATIC(wine);
+#define GST_CAT_DEFAULT wine
+
+static enum wg_audio_format wg_audio_format_from_gst(GstAudioFormat format)
+{
+    switch (format)
+    {
+        case GST_AUDIO_FORMAT_U8:
+            return WG_AUDIO_FORMAT_U8;
+        case GST_AUDIO_FORMAT_S16LE:
+            return WG_AUDIO_FORMAT_S16LE;
+        case GST_AUDIO_FORMAT_S24LE:
+            return WG_AUDIO_FORMAT_S24LE;
+        case GST_AUDIO_FORMAT_S32LE:
+            return WG_AUDIO_FORMAT_S32LE;
+        case GST_AUDIO_FORMAT_F32LE:
+            return WG_AUDIO_FORMAT_F32LE;
+        case GST_AUDIO_FORMAT_F64LE:
+            return WG_AUDIO_FORMAT_F64LE;
+        default:
+            return WG_AUDIO_FORMAT_UNKNOWN;
+    }
+}
+
+static void wg_format_from_audio_info(struct wg_format *format, const GstAudioInfo *info)
+{
+    format->major_type = WG_MAJOR_TYPE_AUDIO;
+    format->u.audio.format = wg_audio_format_from_gst(GST_AUDIO_INFO_FORMAT(info));
+    format->u.audio.channels = GST_AUDIO_INFO_CHANNELS(info);
+    format->u.audio.rate = GST_AUDIO_INFO_RATE(info);
+}
+
+static enum wg_video_format wg_video_format_from_gst(GstVideoFormat format)
+{
+    switch (format)
+    {
+        case GST_VIDEO_FORMAT_BGRA:
+            return WG_VIDEO_FORMAT_BGRA;
+        case GST_VIDEO_FORMAT_BGRx:
+            return WG_VIDEO_FORMAT_BGRx;
+        case GST_VIDEO_FORMAT_BGR:
+            return WG_VIDEO_FORMAT_BGR;
+        case GST_VIDEO_FORMAT_RGB15:
+            return WG_VIDEO_FORMAT_RGB15;
+        case GST_VIDEO_FORMAT_AYUV:
+            return WG_VIDEO_FORMAT_AYUV;
+        case GST_VIDEO_FORMAT_I420:
+            return WG_VIDEO_FORMAT_I420;
+        case GST_VIDEO_FORMAT_NV12:
+            return WG_VIDEO_FORMAT_NV12;
+        case GST_VIDEO_FORMAT_UYVY:
+            return WG_VIDEO_FORMAT_UYVY;
+        case GST_VIDEO_FORMAT_YUY2:
+            return WG_VIDEO_FORMAT_YUY2;
+        case GST_VIDEO_FORMAT_YV12:
+            return WG_VIDEO_FORMAT_YV12;
+        case GST_VIDEO_FORMAT_YVYU:
+            return WG_VIDEO_FORMAT_YVYU;
+        default:
+            return WG_VIDEO_FORMAT_UNKNOWN;
+    }
+}
+
+static void wg_format_from_video_info(struct wg_format *format, const GstVideoInfo *info)
+{
+    format->major_type = WG_MAJOR_TYPE_VIDEO;
+    format->u.video.format = wg_video_format_from_gst(GST_VIDEO_INFO_FORMAT(info));
+    format->u.video.width = GST_VIDEO_INFO_WIDTH(info);
+    format->u.video.height = GST_VIDEO_INFO_HEIGHT(info);
+    format->u.video.fps_n = GST_VIDEO_INFO_FPS_N(info);
+    format->u.video.fps_d = GST_VIDEO_INFO_FPS_D(info);
+}
+
+static void wg_format_from_caps_audio_mpeg(struct wg_format *format, const GstCaps *caps)
+{
+    const GstStructure *structure = gst_caps_get_structure(caps, 0);
+    gint layer, channels, rate;
+
+    if (!gst_structure_get_int(structure, "layer", &layer))
+    {
+        GST_WARNING("Missing \"layer\" value.");
+        return;
+    }
+    if (!gst_structure_get_int(structure, "channels", &channels))
+    {
+        GST_WARNING("Missing \"channels\" value.");
+        return;
+    }
+    if (!gst_structure_get_int(structure, "rate", &rate))
+    {
+        GST_WARNING("Missing \"rate\" value.");
+        return;
+    }
+
+    format->major_type = WG_MAJOR_TYPE_AUDIO;
+
+    if (layer == 1)
+        format->u.audio.format = WG_AUDIO_FORMAT_MPEG1_LAYER1;
+    else if (layer == 2)
+        format->u.audio.format = WG_AUDIO_FORMAT_MPEG1_LAYER2;
+    else if (layer == 3)
+        format->u.audio.format = WG_AUDIO_FORMAT_MPEG1_LAYER3;
+
+    format->u.audio.channels = channels;
+    format->u.audio.rate = rate;
+}
+
+static void wg_format_from_caps_video_cinepak(struct wg_format *format, const GstCaps *caps)
+{
+    const GstStructure *structure = gst_caps_get_structure(caps, 0);
+    gint width, height, fps_n, fps_d;
+
+    if (!gst_structure_get_int(structure, "width", &width))
+    {
+        GST_WARNING("Missing \"width\" value.");
+        return;
+    }
+    if (!gst_structure_get_int(structure, "height", &height))
+    {
+        GST_WARNING("Missing \"height\" value.");
+        return;
+    }
+    if (!gst_structure_get_fraction(structure, "framerate", &fps_n, &fps_d))
+    {
+        fps_n = 0;
+        fps_d = 1;
+    }
+
+    format->major_type = WG_MAJOR_TYPE_VIDEO;
+    format->u.video.format = WG_VIDEO_FORMAT_CINEPAK;
+    format->u.video.width = width;
+    format->u.video.height = height;
+    format->u.video.fps_n = fps_n;
+    format->u.video.fps_d = fps_d;
+}
+
+static void wg_format_from_caps(struct wg_format *format, const GstCaps *caps)
+{
+    const GstStructure *structure = gst_caps_get_structure(caps, 0);
+    const char *name = gst_structure_get_name(structure);
+
+    memset(format, 0, sizeof(*format));
+
+    if (!strcmp(name, "audio/x-raw"))
+    {
+        GstAudioInfo info;
+
+        if (gst_audio_info_from_caps(&info, caps))
+            wg_format_from_audio_info(format, &info);
+    }
+    else if (!strcmp(name, "video/x-raw"))
+    {
+        GstVideoInfo info;
+
+        if (gst_video_info_from_caps(&info, caps))
+            wg_format_from_video_info(format, &info);
+    }
+    else if (!strcmp(name, "audio/mpeg"))
+    {
+        wg_format_from_caps_audio_mpeg(format, caps);
+    }
+    else if (!strcmp(name, "video/x-cinepak"))
+    {
+        wg_format_from_caps_video_cinepak(format, caps);
+    }
+    else
+    {
+        gchar *str = gst_caps_to_string(caps);
+
+        GST_FIXME("Unhandled caps %s.", str);
+        g_free(str);
+    }
+}
+
+static GstAudioFormat wg_audio_format_to_gst(enum wg_audio_format format)
+{
+    switch (format)
+    {
+        case WG_AUDIO_FORMAT_U8:    return GST_AUDIO_FORMAT_U8;
+        case WG_AUDIO_FORMAT_S16LE: return GST_AUDIO_FORMAT_S16LE;
+        case WG_AUDIO_FORMAT_S24LE: return GST_AUDIO_FORMAT_S24LE;
+        case WG_AUDIO_FORMAT_S32LE: return GST_AUDIO_FORMAT_S32LE;
+        case WG_AUDIO_FORMAT_F32LE: return GST_AUDIO_FORMAT_F32LE;
+        case WG_AUDIO_FORMAT_F64LE: return GST_AUDIO_FORMAT_F64LE;
+        default: return GST_AUDIO_FORMAT_UNKNOWN;
+    }
+}
+
+static GstCaps *wg_format_to_caps_audio(const struct wg_format *format)
+{
+    GstAudioFormat audio_format;
+    GstAudioInfo info;
+
+    if ((audio_format = wg_audio_format_to_gst(format->u.audio.format)) == GST_AUDIO_FORMAT_UNKNOWN)
+        return NULL;
+
+    gst_audio_info_set_format(&info, audio_format, format->u.audio.rate, format->u.audio.channels, NULL);
+    return gst_audio_info_to_caps(&info);
+}
+
+static GstVideoFormat wg_video_format_to_gst(enum wg_video_format format)
+{
+    switch (format)
+    {
+        case WG_VIDEO_FORMAT_BGRA:  return GST_VIDEO_FORMAT_BGRA;
+        case WG_VIDEO_FORMAT_BGRx:  return GST_VIDEO_FORMAT_BGRx;
+        case WG_VIDEO_FORMAT_BGR:   return GST_VIDEO_FORMAT_BGR;
+        case WG_VIDEO_FORMAT_RGB15: return GST_VIDEO_FORMAT_RGB15;
+        case WG_VIDEO_FORMAT_RGB16: return GST_VIDEO_FORMAT_RGB16;
+        case WG_VIDEO_FORMAT_AYUV:  return GST_VIDEO_FORMAT_AYUV;
+        case WG_VIDEO_FORMAT_I420:  return GST_VIDEO_FORMAT_I420;
+        case WG_VIDEO_FORMAT_NV12:  return GST_VIDEO_FORMAT_NV12;
+        case WG_VIDEO_FORMAT_UYVY:  return GST_VIDEO_FORMAT_UYVY;
+        case WG_VIDEO_FORMAT_YUY2:  return GST_VIDEO_FORMAT_YUY2;
+        case WG_VIDEO_FORMAT_YV12:  return GST_VIDEO_FORMAT_YV12;
+        case WG_VIDEO_FORMAT_YVYU:  return GST_VIDEO_FORMAT_YVYU;
+        default: return GST_VIDEO_FORMAT_UNKNOWN;
+    }
+}
+
+static GstCaps *wg_format_to_caps_video(const struct wg_format *format)
+{
+    GstVideoFormat video_format;
+    GstVideoInfo info;
+    unsigned int i;
+    GstCaps *caps;
+
+    if ((video_format = wg_video_format_to_gst(format->u.video.format)) == GST_VIDEO_FORMAT_UNKNOWN)
+        return NULL;
+
+    gst_video_info_set_format(&info, video_format, format->u.video.width, format->u.video.height);
+    if ((caps = gst_video_info_to_caps(&info)))
+    {
+        /* Clear some fields that shouldn't prevent us from connecting. */
+        for (i = 0; i < gst_caps_get_size(caps); ++i)
+        {
+            gst_structure_remove_fields(gst_caps_get_structure(caps, i),
+                    "framerate", "pixel-aspect-ratio", "colorimetry", "chroma-site", NULL);
+        }
+    }
+    return caps;
+}
+
+static GstCaps *wg_format_to_caps(const struct wg_format *format)
+{
+    switch (format->major_type)
+    {
+        case WG_MAJOR_TYPE_UNKNOWN:
+            return NULL;
+        case WG_MAJOR_TYPE_AUDIO:
+            return wg_format_to_caps_audio(format);
+        case WG_MAJOR_TYPE_VIDEO:
+            return wg_format_to_caps_video(format);
+    }
+    assert(0);
+    return NULL;
+}
+
+static bool wg_format_compare(const struct wg_format *a, const struct wg_format *b)
+{
+    if (a->major_type != b->major_type)
+        return false;
+
+    switch (a->major_type)
+    {
+        case WG_MAJOR_TYPE_UNKNOWN:
+            return false;
+
+        case WG_MAJOR_TYPE_AUDIO:
+            return a->u.audio.format == b->u.audio.format
+                    && a->u.audio.channels == b->u.audio.channels
+                    && a->u.audio.rate == b->u.audio.rate;
+
+        case WG_MAJOR_TYPE_VIDEO:
+            return a->u.video.format == b->u.video.format
+                    && a->u.video.width == b->u.video.width
+                    && a->u.video.height == b->u.video.height
+                    && a->u.video.fps_d * b->u.video.fps_n == a->u.video.fps_n * b->u.video.fps_d;
+    }
+
+    assert(0);
+    return false;
+}
+
+static GstAutoplugSelectResult autoplug_blacklist(GstElement *bin, GstPad *pad, GstCaps *caps, GstElementFactory *fact, gpointer user)
+{
+    const char *name = gst_element_factory_get_longname(fact);
+
+    GST_TRACE("Using \"%s\".", name);
+
+    if (strstr(name, "Player protection"))
+    {
+        GST_WARNING("Blacklisted a/52 decoder because it only works in Totem.");
+        return GST_AUTOPLUG_SELECT_SKIP;
+    }
+    if (!strcmp(name, "Fluendo Hardware Accelerated Video Decoder"))
+    {
+        GST_WARNING("Disabled video acceleration since it breaks in wine.");
+        return GST_AUTOPLUG_SELECT_SKIP;
+    }
+    return GST_AUTOPLUG_SELECT_TRY;
+}
+
+static void no_more_pads(GstElement *element, gpointer user)
+{
+    struct wg_parser *parser = user;
+
+    GST_DEBUG("parser %p.", parser);
+
+    pthread_mutex_lock(&parser->mutex);
+    parser->no_more_pads = true;
+    pthread_mutex_unlock(&parser->mutex);
+    pthread_cond_signal(&parser->init_cond);
+}
+
+static GstFlowReturn queue_stream_event(struct wg_parser_stream *stream, const struct wg_parser_event *event)
+{
+    struct wg_parser *parser = stream->parser;
+
+    /* Unlike request_buffer_src() [q.v.], we need to watch for GStreamer
+     * flushes here. The difference is that we can be blocked by the streaming
+     * thread not running (or itself flushing on the DirectShow side).
+     * request_buffer_src() can only be blocked by the upstream source, and that
+     * is solved by flushing the upstream source. */
+
+    pthread_mutex_lock(&parser->mutex);
+    while (!stream->flushing && stream->event.type != WG_PARSER_EVENT_NONE)
+        pthread_cond_wait(&stream->event_empty_cond, &parser->mutex);
+    if (stream->flushing)
+    {
+        pthread_mutex_unlock(&parser->mutex);
+        GST_DEBUG("Filter is flushing; discarding event.");
+        return GST_FLOW_FLUSHING;
+    }
+    stream->event = *event;
+    pthread_mutex_unlock(&parser->mutex);
+    pthread_cond_signal(&stream->event_cond);
+    GST_LOG("Event queued.");
+    return GST_FLOW_OK;
+}
+
+static gboolean event_sink(GstPad *pad, GstObject *parent, GstEvent *event)
+{
+    struct wg_parser_stream *stream = gst_pad_get_element_private(pad);
+    struct wg_parser *parser = stream->parser;
+
+    GST_LOG("stream %p, type \"%s\".", stream, GST_EVENT_TYPE_NAME(event));
+
+    switch (event->type)
+    {
+        case GST_EVENT_SEGMENT:
+            if (stream->enabled)
+            {
+                struct wg_parser_event stream_event;
+                const GstSegment *segment;
+
+                gst_event_parse_segment(event, &segment);
+
+                if (segment->format != GST_FORMAT_TIME)
+                {
+                    GST_FIXME("Unhandled format \"%s\".", gst_format_get_name(segment->format));
+                    break;
+                }
+
+                stream_event.type = WG_PARSER_EVENT_SEGMENT;
+                stream_event.u.segment.position = segment->position / 100;
+                stream_event.u.segment.stop = segment->stop / 100;
+                stream_event.u.segment.rate = segment->rate * segment->applied_rate;
+                queue_stream_event(stream, &stream_event);
+            }
+            break;
+
+        case GST_EVENT_EOS:
+            if (stream->enabled)
+            {
+                struct wg_parser_event stream_event;
+
+                stream_event.type = WG_PARSER_EVENT_EOS;
+                queue_stream_event(stream, &stream_event);
+            }
+            else
+            {
+                pthread_mutex_lock(&parser->mutex);
+                stream->eos = true;
+                pthread_mutex_unlock(&parser->mutex);
+                pthread_cond_signal(&parser->init_cond);
+            }
+            break;
+
+        case GST_EVENT_FLUSH_START:
+            if (stream->enabled)
+            {
+                pthread_mutex_lock(&parser->mutex);
+
+                stream->flushing = true;
+                pthread_cond_signal(&stream->event_empty_cond);
+
+                switch (stream->event.type)
+                {
+                    case WG_PARSER_EVENT_NONE:
+                    case WG_PARSER_EVENT_EOS:
+                    case WG_PARSER_EVENT_SEGMENT:
+                        break;
+
+                    case WG_PARSER_EVENT_BUFFER:
+                        gst_buffer_unref(stream->event.u.buffer);
+                        break;
+                }
+                stream->event.type = WG_PARSER_EVENT_NONE;
+
+                pthread_mutex_unlock(&parser->mutex);
+            }
+            break;
+
+        case GST_EVENT_FLUSH_STOP:
+            if (stream->enabled)
+            {
+                pthread_mutex_lock(&parser->mutex);
+                stream->flushing = false;
+                pthread_mutex_unlock(&parser->mutex);
+            }
+            break;
+
+        case GST_EVENT_CAPS:
+        {
+            GstCaps *caps;
+
+            gst_event_parse_caps(event, &caps);
+            pthread_mutex_lock(&parser->mutex);
+            wg_format_from_caps(&stream->preferred_format, caps);
+            stream->has_caps = true;
+            pthread_mutex_unlock(&parser->mutex);
+            pthread_cond_signal(&parser->init_cond);
+            break;
+        }
+
+        default:
+            GST_WARNING("Ignoring \"%s\" event.", GST_EVENT_TYPE_NAME(event));
+    }
+    gst_event_unref(event);
+    return TRUE;
+}
+
+static GstFlowReturn got_data_sink(GstPad *pad, GstObject *parent, GstBuffer *buffer)
+{
+    struct wg_parser_stream *stream = gst_pad_get_element_private(pad);
+    struct wg_parser_event stream_event;
+    GstFlowReturn ret;
+
+    GST_LOG("stream %p, buffer %p.", stream, buffer);
+
+    if (!stream->enabled)
+    {
+        gst_buffer_unref(buffer);
+        return GST_FLOW_OK;
+    }
+
+    stream_event.type = WG_PARSER_EVENT_BUFFER;
+    stream_event.u.buffer = buffer;
+    /* Transfer our reference to the buffer to the object. */
+    if ((ret = queue_stream_event(stream, &stream_event)) != GST_FLOW_OK)
+        gst_buffer_unref(buffer);
+    return ret;
+}
+
+static gboolean query_sink(GstPad *pad, GstObject *parent, GstQuery *query)
+{
+    struct wg_parser_stream *stream = gst_pad_get_element_private(pad);
+
+    GST_LOG("stream %p, type \"%s\".", stream, gst_query_type_get_name(query->type));
+
+    switch (query->type)
+    {
+        case GST_QUERY_CAPS:
+        {
+            GstCaps *caps, *filter, *temp;
+
+            gst_query_parse_caps(query, &filter);
+
+            if (stream->enabled)
+                caps = wg_format_to_caps(&stream->current_format);
+            else
+                caps = gst_caps_new_any();
+            if (!caps)
+                return FALSE;
+
+            if (filter)
+            {
+                temp = gst_caps_intersect(caps, filter);
+                gst_caps_unref(caps);
+                caps = temp;
+            }
+
+            gst_query_set_caps_result(query, caps);
+            gst_caps_unref(caps);
+            return TRUE;
+        }
+
+        case GST_QUERY_ACCEPT_CAPS:
+        {
+            struct wg_format format;
+            gboolean ret = TRUE;
+            GstCaps *caps;
+
+            if (!stream->enabled)
+            {
+                gst_query_set_accept_caps_result(query, TRUE);
+                return TRUE;
+            }
+
+            gst_query_parse_accept_caps(query, &caps);
+            wg_format_from_caps(&format, caps);
+            ret = wg_format_compare(&format, &stream->current_format);
+            if (!ret && WARN_ON(gstreamer))
+            {
+                gchar *str = gst_caps_to_string(caps);
+                GST_WARNING("Rejecting caps \"%s\".", str);
+                g_free(str);
+            }
+            gst_query_set_accept_caps_result(query, ret);
+            return TRUE;
+        }
+
+        default:
+            return gst_pad_query_default (pad, parent, query);
+    }
+}
+
+static struct wg_parser_stream *create_stream(struct wg_parser *parser)
+{
+    struct wg_parser_stream *stream, **new_array;
+    char pad_name[19];
+
+    if (!(new_array = realloc(parser->streams, (parser->stream_count + 1) * sizeof(*parser->streams))))
+        return NULL;
+    parser->streams = new_array;
+
+    if (!(stream = calloc(1, sizeof(*stream))))
+        return NULL;
+
+    stream->parser = parser;
+    pthread_cond_init(&stream->event_cond, NULL);
+    pthread_cond_init(&stream->event_empty_cond, NULL);
+
+    sprintf(pad_name, "qz_sink_%u", parser->stream_count);
+    stream->my_sink = gst_pad_new(pad_name, GST_PAD_SINK);
+    gst_pad_set_element_private(stream->my_sink, stream);
+    gst_pad_set_chain_function(stream->my_sink, got_data_sink);
+    gst_pad_set_event_function(stream->my_sink, event_sink);
+    gst_pad_set_query_function(stream->my_sink, query_sink);
+
+    parser->streams[parser->stream_count++] = stream;
+    return stream;
+}
+
+static void init_new_decoded_pad(GstElement *element, GstPad *pad, struct wg_parser *parser)
+{
+    struct wg_parser_stream *stream;
+    const char *name;
+    GstCaps *caps;
+    int ret;
+
+    caps = gst_caps_make_writable(gst_pad_query_caps(pad, NULL));
+    name = gst_structure_get_name(gst_caps_get_structure(caps, 0));
+
+    if (!(stream = create_stream(parser)))
+        goto out;
+
+    if (!strcmp(name, "video/x-raw"))
+    {
+        GstElement *deinterlace, *vconv, *flip, *vconv2;
+
+        /* DirectShow can express interlaced video, but downstream filters can't
+         * necessarily consume it. In particular, the video renderer can't. */
+        if (!(deinterlace = gst_element_factory_make("deinterlace", NULL)))
+        {
+            fprintf(stderr, "winegstreamer: failed to create deinterlace, are %u-bit GStreamer \"good\" plugins installed?\n",
+                    8 * (int)sizeof(void *));
+            goto out;
+        }
+
+        /* decodebin considers many YUV formats to be "raw", but some quartz
+         * filters can't handle those. Also, videoflip can't handle all "raw"
+         * formats either. Add a videoconvert to swap color spaces. */
+        if (!(vconv = gst_element_factory_make("videoconvert", NULL)))
+        {
+            fprintf(stderr, "winegstreamer: failed to create videoconvert, are %u-bit GStreamer \"base\" plugins installed?\n",
+                    8 * (int)sizeof(void *));
+            goto out;
+        }
+
+        /* GStreamer outputs RGB video top-down, but DirectShow expects bottom-up. */
+        if (!(flip = gst_element_factory_make("videoflip", NULL)))
+        {
+            fprintf(stderr, "winegstreamer: failed to create videoflip, are %u-bit GStreamer \"good\" plugins installed?\n",
+                    8 * (int)sizeof(void *));
+            goto out;
+        }
+
+        /* videoflip does not support 15 and 16-bit RGB so add a second videoconvert
+         * to do the final conversion. */
+        if (!(vconv2 = gst_element_factory_make("videoconvert", NULL)))
+        {
+            fprintf(stderr, "winegstreamer: failed to create videoconvert, are %u-bit GStreamer \"base\" plugins installed?\n",
+                    8 * (int)sizeof(void *));
+            goto out;
+        }
+
+        /* The bin takes ownership of these elements. */
+        gst_bin_add(GST_BIN(parser->container), deinterlace);
+        gst_element_sync_state_with_parent(deinterlace);
+        gst_bin_add(GST_BIN(parser->container), vconv);
+        gst_element_sync_state_with_parent(vconv);
+        gst_bin_add(GST_BIN(parser->container), flip);
+        gst_element_sync_state_with_parent(flip);
+        gst_bin_add(GST_BIN(parser->container), vconv2);
+        gst_element_sync_state_with_parent(vconv2);
+
+        gst_element_link(deinterlace, vconv);
+        gst_element_link(vconv, flip);
+        gst_element_link(flip, vconv2);
+
+        stream->post_sink = gst_element_get_static_pad(deinterlace, "sink");
+        stream->post_src = gst_element_get_static_pad(vconv2, "src");
+        stream->flip = flip;
+    }
+    else if (!strcmp(name, "audio/x-raw"))
+    {
+        GstElement *convert;
+
+        /* Currently our dsound can't handle 64-bit formats or all
+         * surround-sound configurations. Native dsound can't always handle
+         * 64-bit formats either. Add an audioconvert to allow changing bit
+         * depth and channel count. */
+        if (!(convert = gst_element_factory_make("audioconvert", NULL)))
+        {
+            fprintf(stderr, "winegstreamer: failed to create audioconvert, are %u-bit GStreamer \"base\" plugins installed?\n",
+                    8 * (int)sizeof(void *));
+            goto out;
+        }
+
+        gst_bin_add(GST_BIN(parser->container), convert);
+        gst_element_sync_state_with_parent(convert);
+
+        stream->post_sink = gst_element_get_static_pad(convert, "sink");
+        stream->post_src = gst_element_get_static_pad(convert, "src");
+    }
+
+    if (stream->post_sink)
+    {
+        if ((ret = gst_pad_link(pad, stream->post_sink)) < 0)
+        {
+            GST_ERROR("Failed to link decodebin source pad to post-processing elements, error %s.",
+                    gst_pad_link_get_name(ret));
+            gst_object_unref(stream->post_sink);
+            stream->post_sink = NULL;
+            goto out;
+        }
+
+        if ((ret = gst_pad_link(stream->post_src, stream->my_sink)) < 0)
+        {
+            GST_ERROR("Failed to link post-processing elements to our sink pad, error %s.",
+                    gst_pad_link_get_name(ret));
+            gst_object_unref(stream->post_src);
+            stream->post_src = NULL;
+            gst_object_unref(stream->post_sink);
+            stream->post_sink = NULL;
+            goto out;
+        }
+    }
+    else if ((ret = gst_pad_link(pad, stream->my_sink)) < 0)
+    {
+        GST_ERROR("Failed to link decodebin source pad to our sink pad, error %s.",
+                gst_pad_link_get_name(ret));
+        goto out;
+    }
+
+    gst_pad_set_active(stream->my_sink, 1);
+    gst_object_ref(stream->their_src = pad);
+out:
+    gst_caps_unref(caps);
+}
+
+static void existing_new_pad(GstElement *element, GstPad *pad, gpointer user)
+{
+    struct wg_parser *parser = user;
+
+    GST_LOG("parser %p, element %p, pad %p.", parser, element, pad);
+
+    if (gst_pad_is_linked(pad))
+        return;
+
+    init_new_decoded_pad(element, pad, parser);
+}
+
+static void removed_decoded_pad(GstElement *element, GstPad *pad, gpointer user)
+{
+    struct wg_parser *parser = user;
+    unsigned int i;
+    char *name;
+
+    GST_LOG("parser %p, element %p, pad %p.", parser, element, pad);
+
+    for (i = 0; i < parser->stream_count; ++i)
+    {
+        struct wg_parser_stream *stream = parser->streams[i];
+
+        if (stream->their_src == pad)
+        {
+            if (stream->post_sink)
+                gst_pad_unlink(stream->their_src, stream->post_sink);
+            else
+                gst_pad_unlink(stream->their_src, stream->my_sink);
+            gst_object_unref(stream->their_src);
+            stream->their_src = NULL;
+            return;
+        }
+    }
+
+    name = gst_pad_get_name(pad);
+    GST_WARNING("No pin matching pad \"%s\" found.", name);
+    g_free(name);
+}
+
+static BOOL decodebin_parser_init_gst(struct wg_parser *parser)
+{
+    GstElement *element = gst_element_factory_make("decodebin", NULL);
+    int ret;
+
+    if (!element)
+    {
+        ERR("Failed to create decodebin; are %u-bit GStreamer \"base\" plugins installed?\n",
+                8 * (int)sizeof(void*));
+        return FALSE;
+    }
+
+    gst_bin_add(GST_BIN(parser->container), 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);
+    g_signal_connect(element, "autoplug-select", G_CALLBACK(autoplug_blacklist), parser);
+    g_signal_connect(element, "no-more-pads", G_CALLBACK(no_more_pads), parser);
+
+    parser->their_sink = gst_element_get_static_pad(element, "sink");
+
+    pthread_mutex_lock(&parser->mutex);
+    parser->no_more_pads = parser->error = false;
+    pthread_mutex_unlock(&parser->mutex);
+
+    if ((ret = gst_pad_link(parser->my_src, parser->their_sink)) < 0)
+    {
+        ERR("Failed to link pads, error %d.\n", ret);
+        return FALSE;
+    }
+
+    gst_element_set_state(parser->container, GST_STATE_PAUSED);
+    ret = gst_element_get_state(parser->container, NULL, NULL, -1);
+    if (ret == GST_STATE_CHANGE_FAILURE)
+    {
+        ERR("Failed to play stream.\n");
+        return FALSE;
+    }
+
+    pthread_mutex_lock(&parser->mutex);
+    while (!parser->no_more_pads && !parser->error)
+        pthread_cond_wait(&parser->init_cond, &parser->mutex);
+    if (parser->error)
+    {
+        pthread_mutex_unlock(&parser->mutex);
+        return FALSE;
+    }
+    pthread_mutex_unlock(&parser->mutex);
+
+    return TRUE;
+}
+
+static BOOL avi_parser_init_gst(struct wg_parser *parser)
+{
+    GstElement *element = gst_element_factory_make("avidemux", NULL);
+    int ret;
+
+    if (!element)
+    {
+        ERR("Failed to create avidemux; are %u-bit GStreamer \"good\" plugins installed?\n",
+                8 * (int)sizeof(void*));
+        return FALSE;
+    }
+
+    gst_bin_add(GST_BIN(parser->container), 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);
+    g_signal_connect(element, "no-more-pads", G_CALLBACK(no_more_pads), parser);
+
+    parser->their_sink = gst_element_get_static_pad(element, "sink");
+
+    pthread_mutex_lock(&parser->mutex);
+    parser->no_more_pads = parser->error = false;
+    pthread_mutex_unlock(&parser->mutex);
+
+    if ((ret = gst_pad_link(parser->my_src, parser->their_sink)) < 0)
+    {
+        ERR("Failed to link pads, error %d.\n", ret);
+        return FALSE;
+    }
+
+    gst_element_set_state(parser->container, GST_STATE_PAUSED);
+    ret = gst_element_get_state(parser->container, NULL, NULL, -1);
+    if (ret == GST_STATE_CHANGE_FAILURE)
+    {
+        ERR("Failed to play stream.\n");
+        return FALSE;
+    }
+
+    pthread_mutex_lock(&parser->mutex);
+    while (!parser->no_more_pads && !parser->error)
+        pthread_cond_wait(&parser->init_cond, &parser->mutex);
+    if (parser->error)
+    {
+        pthread_mutex_unlock(&parser->mutex);
+        return FALSE;
+    }
+    pthread_mutex_unlock(&parser->mutex);
+
+    return TRUE;
+}
+
+static BOOL mpeg_audio_parser_init_gst(struct wg_parser *parser)
+{
+    struct wg_parser_stream *stream;
+    GstElement *element;
+    int ret;
+
+    if (!(element = gst_element_factory_make("mpegaudioparse", NULL)))
+    {
+        ERR("Failed to create mpegaudioparse; are %u-bit GStreamer \"good\" plugins installed?\n",
+                8 * (int)sizeof(void*));
+        return FALSE;
+    }
+
+    gst_bin_add(GST_BIN(parser->container), element);
+
+    parser->their_sink = gst_element_get_static_pad(element, "sink");
+    if ((ret = gst_pad_link(parser->my_src, parser->their_sink)) < 0)
+    {
+        ERR("Failed to link sink pads, error %d.\n", ret);
+        return FALSE;
+    }
+
+    if (!(stream = create_stream(parser)))
+        return FALSE;
+
+    gst_object_ref(stream->their_src = gst_element_get_static_pad(element, "src"));
+    if ((ret = gst_pad_link(stream->their_src, stream->my_sink)) < 0)
+    {
+        ERR("Failed to link source pads, error %d.\n", ret);
+        return FALSE;
+    }
+
+    gst_pad_set_active(stream->my_sink, 1);
+    gst_element_set_state(parser->container, GST_STATE_PAUSED);
+    ret = gst_element_get_state(parser->container, NULL, NULL, -1);
+    if (ret == GST_STATE_CHANGE_FAILURE)
+    {
+        ERR("Failed to play stream.\n");
+        return FALSE;
+    }
+
+    pthread_mutex_lock(&parser->mutex);
+    while (!parser->has_duration && !parser->error && !stream->eos)
+        pthread_cond_wait(&parser->init_cond, &parser->mutex);
+    if (parser->error)
+    {
+        pthread_mutex_unlock(&parser->mutex);
+        return FALSE;
+    }
+    pthread_mutex_unlock(&parser->mutex);
+    return TRUE;
+}
+
+static BOOL wave_parser_init_gst(struct wg_parser *parser)
+{
+    struct wg_parser_stream *stream;
+    GstElement *element;
+    int ret;
+
+    if (!(element = gst_element_factory_make("wavparse", NULL)))
+    {
+        ERR("Failed to create wavparse; are %u-bit GStreamer \"good\" plugins installed?\n",
+                8 * (int)sizeof(void*));
+        return FALSE;
+    }
+
+    gst_bin_add(GST_BIN(parser->container), element);
+
+    parser->their_sink = gst_element_get_static_pad(element, "sink");
+    if ((ret = gst_pad_link(parser->my_src, parser->their_sink)) < 0)
+    {
+        ERR("Failed to link sink pads, error %d.\n", ret);
+        return FALSE;
+    }
+
+    if (!(stream = create_stream(parser)))
+        return FALSE;
+
+    stream->their_src = gst_element_get_static_pad(element, "src");
+    gst_object_ref(stream->their_src);
+    if ((ret = gst_pad_link(stream->their_src, stream->my_sink)) < 0)
+    {
+        ERR("Failed to link source pads, error %d.\n", ret);
+        return FALSE;
+    }
+
+    gst_pad_set_active(stream->my_sink, 1);
+    gst_element_set_state(parser->container, GST_STATE_PAUSED);
+    ret = gst_element_get_state(parser->container, NULL, NULL, -1);
+    if (ret == GST_STATE_CHANGE_FAILURE)
+    {
+        ERR("Failed to play stream.\n");
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+static struct wg_parser *wg_parser_create(void)
+{
+    struct wg_parser *parser;
+
+    if (!(parser = calloc(1, sizeof(*parser))))
+        return NULL;
+
+    pthread_mutex_init(&parser->mutex, NULL);
+    pthread_cond_init(&parser->init_cond, NULL);
+    pthread_cond_init(&parser->read_cond, NULL);
+    pthread_cond_init(&parser->read_done_cond, NULL);
+    parser->flushing = true;
+
+    TRACE("Created winegstreamer parser %p.\n", parser);
+    return parser;
+}
+
+static struct wg_parser * CDECL wg_decodebin_parser_create(void)
+{
+    struct wg_parser *parser;
+
+    if ((parser = wg_parser_create()))
+        parser->init_gst = decodebin_parser_init_gst;
+    return parser;
+}
+
+static struct wg_parser * CDECL wg_avi_parser_create(void)
+{
+    struct wg_parser *parser;
+
+    if ((parser = wg_parser_create()))
+        parser->init_gst = avi_parser_init_gst;
+    return parser;
+}
+
+static struct wg_parser * CDECL wg_mpeg_audio_parser_create(void)
+{
+    struct wg_parser *parser;
+
+    if ((parser = wg_parser_create()))
+        parser->init_gst = mpeg_audio_parser_init_gst;
+    return parser;
+}
+
+static struct wg_parser * CDECL wg_wave_parser_create(void)
+{
+    struct wg_parser *parser;
+
+    if ((parser = wg_parser_create()))
+        parser->init_gst = wave_parser_init_gst;
+    return parser;
+}
+
+static const struct unix_funcs funcs =
+{
+    wg_decodebin_parser_create,
+    wg_avi_parser_create,
+    wg_mpeg_audio_parser_create,
+    wg_wave_parser_create,
+};
+
+NTSTATUS CDECL __wine_init_unix_lib(HMODULE module, DWORD reason, const void *ptr_in, void *ptr_out)
+{
+    if (reason == DLL_PROCESS_ATTACH)
+    {
+        GST_DEBUG_CATEGORY_INIT(wine, "WINE", GST_DEBUG_FG_RED, "Wine GStreamer support");
+        *(const struct unix_funcs **)ptr_out = &funcs;
+    }
+    return STATUS_SUCCESS;
+}
-- 
2.30.0




More information about the wine-devel mailing list